Print this page
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/auditreduce/main.c
+++ new/usr/src/cmd/auditreduce/main.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 + *
26 + * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
25 27 */
26 28
27 29 /*
28 30 * The Secure SunOS audit reduction tool - auditreduce.
29 31 * Document SM0071 is the primary source of information on auditreduce.
30 32 *
31 33 * Composed of 4 source modules:
32 34 * main.c - main driver.
33 35 * option.c - command line option processing.
34 36 * process.c - record/file/process functions.
35 37 * time.c - date/time handling.
36 38 *
37 39 * Main(), write_header(), audit_stats(), and a_calloc()
38 40 * are the only functions visible outside this module.
39 41 */
40 42
41 43 #include <siginfo.h>
42 44 #include <locale.h>
43 45 #include <libintl.h>
44 46 #include "auditr.h"
45 47 #include "auditrd.h"
46 48
47 49 #if !defined(TEXT_DOMAIN)
48 50 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
49 51 #endif
50 52
51 53 extern void derive_str(time_t, char *);
52 54 extern int process_options(int, char **);
53 55 extern int mproc(audit_pcb_t *);
54 56 extern void init_tokens(void); /* shared with praudit */
55 57
56 58 static int a_pow(int, int);
57 59 static void calc_procs(void);
58 60 static void chld_handler(int);
59 61 static int close_outfile(void);
60 62 static void c_close(audit_pcb_t *, int);
61 63 static void delete_infiles(void);
62 64 static void gather_pcb(audit_pcb_t *, int, int);
63 65 static void init_options(void);
64 66 static int init_sig(void);
65 67 static void int_handler(int);
66 68 static int mfork(audit_pcb_t *, int, int, int);
67 69 static void mcount(int, int);
68 70 static int open_outfile(void);
69 71 static void p_close(audit_pcb_t *);
70 72 static int rename_outfile(void);
71 73 static void rm_mem(audit_pcb_t *);
72 74 static void rm_outfile(void);
73 75 static void trim_mem(audit_pcb_t *);
|
↓ open down ↓ |
39 lines elided |
↑ open up ↑ |
74 76 static int write_file_token(time_t);
75 77 static int write_trailer(void);
76 78
77 79 /*
78 80 * File globals.
79 81 */
80 82 static int max_sproc; /* maximum number of subprocesses per process */
81 83 static int total_procs; /* number of processes in the process tree */
82 84 static int total_layers; /* number of layers in the process tree */
83 85
86 +char errbuf[ERRBUF_SZ]; /* for creating error messages with sprintf */
87 +
84 88 /*
85 89 * .func main - main.
86 90 * .desc The beginning. Main() calls each of the initialization routines
87 91 * and then allocates the root pcb. Then it calls mfork() to get
88 92 * the work done.
89 93 * .call main(argc, argv).
90 94 * .arg argc - number of arguments.
91 95 * .arg argv - array of pointers to arguments.
92 96 * .ret 0 - via exit() - no errors detected.
93 97 * .ret 1 - via exit() - errors detected (messages printed).
94 98 */
95 99 int
96 100 main(int argc, char **argv)
97 101 {
98 102 int ret;
99 103 audit_pcb_t *pcb;
100 104
101 105 /* Internationalization */
102 106 (void) setlocale(LC_ALL, "");
103 107 (void) textdomain(TEXT_DOMAIN);
104 108
105 109 root_pid = getpid(); /* know who is root process for error */
106 110 init_options(); /* initialize options */
107 111 init_tokens(); /* initialize token processing table */
108 112 if (init_sig()) /* initialize signals */
109 113 exit(1);
110 114 if (process_options(argc, argv))
111 115 exit(1); /* process command line options */
112 116 if (open_outfile()) /* setup root process output stream */
113 117 exit(1);
114 118 calc_procs(); /* see how many subprocesses we need */
115 119 /*
116 120 * Allocate the root pcb and set it up.
117 121 */
118 122 pcb = (audit_pcb_t *)a_calloc(1, sizeof (audit_pcb_t));
119 123 pcb->pcb_procno = root_pid;
120 124 pcb->pcb_flags |= PF_ROOT;
121 125 pcb->pcb_fpw = stdout;
122 126 pcb->pcb_time = -1;
123 127 /*
124 128 * Now start the whole thing rolling.
125 129 */
126 130 if (mfork(pcb, pcbnum, 0, pcbnum - 1)) {
127 131 /*
128 132 * Error in processing somewhere. A message is already printed.
129 133 * Display usage statistics and remove the outfile.
130 134 */
131 135 if (getpid() == root_pid) {
132 136 audit_stats();
133 137 (void) close_outfile();
134 138 rm_outfile();
135 139 }
136 140 exit(1);
137 141 }
138 142 /*
139 143 * Clean up afterwards.
140 144 * Only do outfile cleanup if we are root process.
141 145 */
142 146 if (getpid() == root_pid) {
143 147 if ((ret = write_trailer()) == 0) { /* write trailer to file */
144 148
145 149 ret = close_outfile(); /* close the outfile */
146 150 }
147 151 /*
148 152 * If there was an error in cleanup then remove outfile.
149 153 */
150 154 if (ret) {
151 155 rm_outfile();
152 156 exit(1);
153 157 }
154 158 /*
155 159 * And lastly delete the infiles if the user so wishes.
156 160 */
157 161 if (f_delete)
158 162 delete_infiles();
159 163 }
160 164 return (0);
161 165 /*NOTREACHED*/
162 166 }
163 167
164 168
165 169 /*
166 170 * .func mfork - main fork routine.
167 171 * .desc Create a (sub-)tree of processses if needed, or just do the work
168 172 * if we have few enough groups to process. This is a recursive routine
169 173 * which stops recursing when the number of files to process is small
170 174 * enough. Each call to mfork() is responsible for a range of pcbs
171 175 * from audit_pcbs[]. This range is designated by the lo and hi
172 176 * arguments (inclusive). If the number of pcbs is small enough
173 177 * then we have hit a leaf of the tree and mproc() is called to
174 178 * do the processing. Otherwise we fork some processes and break
175 179 * the range of pcbs up amongst them.
176 180 * .call ret = mfork(pcb, nsp, lo, hi).
177 181 * .arg pcb - ptr to pcb that is root node of the to-be-created tree.
178 182 * .arg nsp - number of sub-processes this tree must process.
179 183 * .arg lo - lower-limit of process number range. Index into audit_pcbs.
180 184 * .arg hi - higher limit of pcb range. Index into audit_pcbs.
181 185 * .ret 0 - succesful completion.
182 186 * .ret -1 - error encountered in processing - message already printed.
183 187 */
184 188 static int
185 189 mfork(audit_pcb_t *pcb, int nsp, int lo, int hi)
186 190 {
187 191 int range, procno, i, tofork, nnsp, nrem;
188 192 int fildes[2];
189 193 audit_pcb_t *pcbn;
190 194
191 195 #if AUDIT_PROC_TRACE
192 196 (void) fprintf(stderr, "mfork: nsp %d %d->%d\n", nsp, lo, hi);
193 197 #endif
194 198
195 199 /*
196 200 * The range of pcb's to process is small enough now. Do the work.
197 201 */
198 202 if (nsp <= max_sproc) {
199 203 pcb->pcb_flags |= PF_LEAF; /* leaf in process tree */
200 204 pcb->pcb_below = audit_pcbs; /* proc pcbs from audit_pcbs */
201 205 gather_pcb(pcb, lo, hi);
202 206 trim_mem(pcb); /* trim allocated memory */
203 207 return (mproc(pcb)); /* do the work */
204 208 }
205 209 /*
206 210 * Too many pcb's for one process - must fork.
207 211 * Try to balance the tree as it grows and make it short and fat.
208 212 * The thing to minimize is the number of times a record passes
209 213 * through a pipe.
210 214 */
211 215 else {
212 216 /*
213 217 * Fork less than the maximum number of processes.
214 218 */
215 219 if (nsp <= max_sproc * (max_sproc - 1)) {
216 220 tofork = nsp / max_sproc;
217 221 if (nsp % max_sproc)
218 222 tofork++; /* how many to fork */
219 223 }
220 224 /*
221 225 * Fork the maximum number of processes.
222 226 */
223 227 else {
224 228 tofork = max_sproc; /* how many to fork */
225 229 }
226 230 /*
227 231 * Allocate the nodes below us in the process tree.
228 232 */
229 233 pcb->pcb_below = (audit_pcb_t *)
230 234 a_calloc(tofork, sizeof (*pcb));
231 235 nnsp = nsp / tofork; /* # of pcbs per forked process */
232 236 nrem = nsp % tofork; /* remainder to spread around */
233 237 /*
234 238 * Loop to fork all of the subs. Open a pipe for each.
235 239 * If there are any errors in pipes, forks, or getting streams
236 240 * for the pipes then quit altogether.
237 241 */
238 242 for (i = 0; i < tofork; i++) {
239 243 pcbn = &pcb->pcb_below[i];
240 244 pcbn->pcb_time = -1;
241 245 if (pipe(fildes)) {
242 246 perror(gettext(
243 247 "auditreduce: couldn't get a pipe"));
244 248 return (-1);
245 249 }
246 250 /*
247 251 * Convert descriptors to streams.
248 252 */
249 253 if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) {
250 254 perror(gettext("auditreduce: couldn't get read stream for pipe"));
251 255 return (-1);
252 256 }
253 257 if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) {
254 258 perror(gettext("auditreduce: couldn't get write stream for pipe"));
255 259 return (-1);
256 260 }
257 261 if ((procno = fork()) == -1) {
258 262 perror(gettext("auditreduce: fork failed"));
259 263 return (-1);
260 264 }
261 265 /*
262 266 * Calculate the range of pcbs from audit_pcbs [] this
263 267 * branch of the tree will be responsible for.
264 268 */
265 269 range = (nrem > 0) ? nnsp + 1 : nnsp;
266 270 /*
267 271 * Child route.
268 272 */
269 273 if (procno == 0) {
270 274 pcbn->pcb_procno = getpid();
271 275 c_close(pcb, i); /* close unused streams */
272 276 /*
273 277 * Continue resolving this branch.
274 278 */
275 279 return (mfork(pcbn, range, lo, lo + range - 1));
276 280 }
277 281 /* Parent route. */
278 282 else {
279 283 pcbn->pcb_procno = i;
280 284 /* allocate buffer to hold record */
281 285 pcbn->pcb_rec = (char *)a_calloc(1,
282 286 AUDITBUFSIZE);
283 287 pcbn->pcb_size = AUDITBUFSIZE;
284 288 p_close(pcbn); /* close unused streams */
285 289
286 290 nrem--;
287 291 lo += range;
288 292 }
289 293 }
290 294 /*
291 295 * Done forking all of the subs.
292 296 */
293 297 gather_pcb(pcb, 0, tofork - 1);
294 298 trim_mem(pcb); /* free unused memory */
295 299 return (mproc(pcb));
296 300 }
297 301 }
298 302
299 303
300 304 /*
301 305 * .func trim_mem - trim memory usage.
302 306 * .desc Free un-needed allocated memory.
303 307 * .call trim_mem(pcb).
304 308 * .arg pcb - ptr to pcb for current process.
305 309 * .ret void.
306 310 */
307 311 static void
308 312 trim_mem(audit_pcb_t *pcb)
309 313 {
310 314 int count;
311 315 size_t size;
312 316
313 317 /*
314 318 * For the root don't free anything. We need to save audit_pcbs[]
315 319 * in case we are deleting the infiles at the end.
316 320 */
317 321 if (pcb->pcb_flags & PF_ROOT)
318 322 return;
319 323 /*
320 324 * For a leaf save its part of audit_pcbs[] and then remove it all.
321 325 */
322 326 if (pcb->pcb_flags & PF_LEAF) {
323 327 count = pcb->pcb_count;
324 328 size = sizeof (audit_pcb_t);
325 329 /* allocate a new buffer to hold the pcbs */
326 330 pcb->pcb_below = (audit_pcb_t *)a_calloc(count, size);
327 331 /* save this pcb's portion */
328 332 (void) memcpy((void *) pcb->pcb_below,
329 333 (void *) &audit_pcbs[pcb->pcb_lo], count * size);
330 334 rm_mem(pcb);
331 335 gather_pcb(pcb, 0, count - 1);
332 336 }
333 337 /*
334 338 * If this is an intermediate node then just remove it all.
335 339 */
336 340 else {
337 341 rm_mem(pcb);
338 342 }
339 343 }
340 344
341 345
342 346 /*
343 347 * .func rm_mem - remove memory.
344 348 * .desc Remove unused memory associated with audit_pcbs[]. For each
345 349 * pcb in audit_pcbs[] free the record buffer and all of
346 350 * the fcbs. Then free audit_pcbs[].
347 351 * .call rm_mem(pcbr).
348 352 * .arg pcbr - ptr to pcb of current process.
349 353 * .ret void.
350 354 */
351 355 static void
352 356 rm_mem(audit_pcb_t *pcbr)
353 357 {
354 358 int i;
355 359 audit_pcb_t *pcb;
356 360 audit_fcb_t *fcb, *fcbn;
357 361
358 362 for (i = 0; i < pcbsize; i++) {
359 363 /*
360 364 * Don't free the record buffer and fcbs for the pcbs this
361 365 * process is using.
362 366 */
363 367 if (pcbr->pcb_flags & PF_LEAF) {
364 368 if (pcbr->pcb_lo <= i || i <= pcbr->pcb_hi)
365 369 continue;
366 370 }
367 371 pcb = &audit_pcbs[i];
368 372 free(pcb->pcb_rec);
369 373 for (fcb = pcb->pcb_first; fcb != NULL; /* */) {
370 374 fcbn = fcb->fcb_next;
371 375 free((char *)fcb);
372 376 fcb = fcbn;
373 377 }
374 378 }
375 379 free((char *)audit_pcbs);
376 380 }
377 381
378 382
379 383 /*
380 384 * .func c_close - close unused streams.
381 385 * .desc This is called for each child process just after being born.
382 386 * The child closes the read stream for the pipe to its parent.
383 387 * It also closes the read streams for the other children that
384 388 * have been born before it. If any closes fail a warning message
385 389 * is printed, but processing continues.
386 390 * .call ret = c_close(pcb, i).
387 391 * .arg pcb - ptr to the child's parent pcb.
388 392 * .arg i - iteration # of child in forking loop.
389 393 * .ret void.
390 394 */
391 395 static void
392 396 c_close(audit_pcb_t *pcb, int i)
393 397 {
394 398 int j;
395 399 audit_pcb_t *pcbt;
396 400
397 401 /*
398 402 * Do all pcbs in parent's group up to and including us
399 403 */
400 404 for (j = 0; j <= i; j++) {
401 405 pcbt = &pcb->pcb_below[j];
402 406 if (fclose(pcbt->pcb_fpr) == EOF) {
403 407 if (!f_quiet)
404 408 perror(gettext("auditreduce: initial close on pipe failed"));
405 409 }
406 410 /*
407 411 * Free the buffer allocated to hold incoming records.
408 412 */
409 413 if (i != j) {
410 414 free(pcbt->pcb_rec);
411 415 }
412 416 }
413 417 }
414 418
415 419
416 420 /*
417 421 * .func p_close - close unused streams for parent.
418 422 * .desc Called by the parent right after forking a child.
419 423 * Closes the write stream on the pipe to the child since
420 424 * we will never use it.
421 425 * .call p_close(pcbn),
422 426 * .arg pcbn - ptr to pcb.
423 427 * .ret void.
424 428 */
425 429 static void
426 430 p_close(audit_pcb_t *pcbn)
427 431 {
428 432 if (fclose(pcbn->pcb_fpw) == EOF) {
429 433 if (!f_quiet)
430 434 perror(gettext("auditreduce: close for write pipe failed"));
431 435 }
432 436 }
433 437
434 438
435 439 /*
436 440 * .func audit_stats - print statistics.
437 441 * .desc Print usage statistics for the user if the run fails.
438 442 * Tells them how many files they had and how many groups this
439 443 * totalled. Also tell them how many layers and processes the
440 444 * process tree had.
441 445 * .call audit_stats().
442 446 * .arg none.
443 447 * .ret void.
444 448 */
445 449 void
446 450 audit_stats(void)
447 451 {
448 452 struct rlimit rl;
449 453
450 454 if (getrlimit(RLIMIT_NOFILE, &rl) != -1)
451 455 (void) fprintf(stderr,
452 456 gettext("%s The system allows %d files per process.\n"),
453 457 ar, rl.rlim_cur);
454 458 (void) fprintf(stderr, gettext(
455 459 "%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
456 460 ar, filenum, pcbnum, total_procs, total_layers);
457 461 }
458 462
459 463
460 464 /*
461 465 * .func gather_pcb - gather pcbs.
462 466 * .desc Gather together the range of the sub-processes that we are
463 467 * responsible for. For a pcb that controls processes this is all
464 468 * of the sub-processes that it forks. For a pcb that controls
465 469 * files this is the the range of pcbs from audit_pcbs[].
466 470 * .call gather_pcb(pcb, lo, hi).
467 471 * .arg pcb - ptr to pcb.
468 472 * .arg lo - lo index into pcb_below.
469 473 * .arg hi - hi index into pcb_below.
470 474 * .ret void.
471 475 */
472 476 static void
473 477 gather_pcb(audit_pcb_t *pcb, int lo, int hi)
474 478 {
475 479 pcb->pcb_lo = lo;
476 480 pcb->pcb_hi = hi;
477 481 pcb->pcb_count = hi - lo + 1;
478 482 }
479 483
480 484
481 485 /*
482 486 * .func calc_procs - calculate process parameters.
483 487 * .desc Calculate the current run's paramters regarding how many
484 488 * processes will have to be forked (maybe none).
485 489 * 5 is subtracted from maxfiles_proc to allow for stdin, stdout,
486 490 * stderr, and the pipe to a parent process. The outfile
487 491 * in the root process is assigned to stdout. The unused half of each
488 492 * pipe is closed, to allow for more connections, but we still
489 493 * have to have the 5th spot because in order to get the pipe
490 494 * we need 2 descriptors up front.
491 495 * .call calc_procs().
492 496 * .arg none.
493 497 * .ret void.
494 498 */
495 499 static void
496 500 calc_procs(void)
497 501 {
498 502 int val;
499 503 int maxfiles_proc;
500 504 struct rlimit rl;
501 505
502 506 if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
503 507 perror("auditreduce: getrlimit");
504 508 exit(1);
505 509 }
506 510
507 511 maxfiles_proc = rl.rlim_cur;
508 512
509 513 max_sproc = maxfiles_proc - 5; /* max subprocesses per process */
510 514
511 515 /*
512 516 * Calculate how many layers the process tree has.
513 517 */
514 518 total_layers = 1;
515 519 for (/* */; /* */; /* */) {
516 520 val = a_pow(max_sproc, total_layers);
517 521 if (val > pcbnum)
518 522 break;
519 523 total_layers++;
520 524 }
521 525 /*
522 526 * Count how many processes are in the process tree.
523 527 */
524 528 mcount(pcbnum, 0);
525 529
526 530 #if AUDIT_PROC_TRACE
527 531 (void) fprintf(stderr,
528 532 "pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
529 533 pcbnum, filenum, maxfiles_proc, max_sproc,
530 534 total_layers, total_procs);
531 535 #endif
532 536 }
533 537
534 538
535 539 static int
536 540 a_pow(int base, int exp)
537 541 {
538 542 int i;
539 543 int answer;
540 544
541 545 if (exp == 0) {
542 546 answer = 1;
543 547 } else {
544 548 answer = base;
545 549 for (i = 0; i < (exp - 1); i++)
546 550 answer *= base;
547 551 }
548 552 return (answer);
549 553 }
550 554
551 555
552 556 /*
553 557 * .func mcount - main count.
554 558 * .desc Go through the motions of building the process tree just
555 559 * to count how many processes there are. Don't really
556 560 * build anything. Answer is in global var total_procs.
557 561 * .call mcount(nsp, lo).
558 562 * .arg nsp - number of subs for this tree branch.
559 563 * .arg lo - lo side of range of subs.
560 564 * .ret void.
561 565 */
562 566 static void
563 567 mcount(int nsp, int lo)
564 568 {
565 569 int range, i, tofork, nnsp, nrem;
566 570
567 571 total_procs++; /* count another process created */
568 572
569 573 if (nsp > max_sproc) {
570 574 if (nsp <= max_sproc * (max_sproc - 1)) {
571 575 tofork = nsp / max_sproc;
572 576 if (nsp % max_sproc)
573 577 tofork++;
574 578 } else {
575 579 tofork = max_sproc;
576 580 }
577 581 nnsp = nsp / tofork;
578 582 nrem = nsp % tofork;
579 583 for (i = 0; i < tofork; i++) {
580 584 range = (nrem > 0) ? nnsp + 1 : nnsp;
581 585 mcount(range, lo);
582 586 nrem--;
583 587 lo += range;
584 588 }
585 589 }
586 590 }
587 591
588 592
589 593 /*
590 594 * .func delete_infiles - delete the input files.
591 595 * .desc If the user asked us to (via 'D' flag) then unlink the input files.
592 596 * .call ret = delete_infiles().
593 597 * .arg none.
594 598 * .ret void.
595 599 */
596 600 static void
597 601 delete_infiles(void)
598 602 {
599 603 int i;
600 604 audit_pcb_t *pcb;
601 605 audit_fcb_t *fcb;
602 606
603 607 for (i = 0; i < pcbsize; i++) {
604 608 pcb = &audit_pcbs[i];
605 609 fcb = pcb->pcb_dfirst;
606 610 while (fcb != NULL) {
607 611 /*
608 612 * Only delete a file if it was succesfully processed.
609 613 * If there were any read errors or bad records
610 614 * then don't delete it.
611 615 * There may still be unprocessed records in it.
612 616 */
613 617 if (fcb->fcb_flags & FF_DELETE) {
614 618 if (unlink(fcb->fcb_file)) {
615 619 if (f_verbose) {
616 620 (void) sprintf(errbuf, gettext(
617 621 "%s delete on %s failed"),
618 622 ar, fcb->fcb_file);
619 623 }
620 624 perror(errbuf);
621 625 }
622 626 }
623 627 fcb = fcb->fcb_next;
624 628 }
625 629 }
626 630 }
627 631
628 632
629 633 /*
630 634 * .func rm_outfile - remove the outfile.
631 635 * .desc Remove the file we are writing the records to. We do this if
632 636 * processing failed and we are quitting before finishing.
633 637 * Update - don't actually remove the outfile, but generate
634 638 * a warning about its possible heathen nature.
635 639 * .call ret = rm_outfile().
636 640 * .arg none.
637 641 * .ret void.
638 642 */
639 643 static void
640 644 rm_outfile(void)
641 645 {
642 646 #if 0
643 647 if (f_outfile) {
644 648 if (unlink(f_outtemp) == -1) {
645 649 (void) sprintf(errbuf,
646 650 gettext("%s delete on %s failed"),
647 651 ar, f_outtemp);
648 652 perror(errbuf);
649 653 }
650 654 }
651 655 #else
652 656 (void) fprintf(stderr,
653 657 gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
654 658 ar,
655 659 (f_outfile == NULL) ? gettext("standard output") : f_outfile);
656 660 #endif
657 661 }
658 662
659 663
660 664 /*
661 665 * .func close_outfile - close the outfile.
662 666 * .desc Close the file we are writing records to.
663 667 * .call ret = close_outfile().
664 668 * .arg none.
665 669 * .ret 0 - close was succesful.
666 670 * .ret -1 - close failed.
667 671 */
668 672 static int
669 673 close_outfile(void)
670 674 {
671 675 if (fclose(stdout) == EOF) {
672 676 (void) sprintf(errbuf, gettext("%s close on %s failed"),
673 677 ar, f_outfile ? f_outfile : "standard output");
674 678 perror(errbuf);
675 679 return (-1);
676 680 }
677 681 (void) fsync(fileno(stdout));
678 682 return (rename_outfile());
679 683 }
680 684
681 685
682 686 /*
683 687 * .func write_header - write audit file header.
684 688 * .desc Write an audit file header to the output stream. The time in the
685 689 * header is the time of the first record written to the stream. This
686 690 * routine is called by the process handling the root node of the
687 691 * process tree just before it writes the first record to the output
688 692 * stream.
689 693 * .ret 0 - succesful write.
690 694 * .ret -1 - failed write - message printed.
691 695 */
692 696 int
693 697 write_header(void)
694 698 {
695 699 return (write_file_token(f_start));
696 700 }
697 701
698 702
699 703 static int
700 704 write_file_token(time_t when)
701 705 {
702 706 adr_t adr; /* adr ptr */
703 707 struct timeval tv; /* time now */
704 708 char for_adr[16]; /* plenty of room */
705 709 #ifdef _LP64
706 710 char token_id = AUT_OTHER_FILE64;
707 711 #else
708 712 char token_id = AUT_OTHER_FILE32;
709 713 #endif
710 714 short i = 1;
711 715 char c = '\0';
712 716
713 717 tv.tv_sec = when;
714 718 tv.tv_usec = 0;
715 719 adr_start(&adr, for_adr);
716 720 adr_char(&adr, &token_id, 1);
717 721 #ifdef _LP64
718 722 adr_int64(&adr, (int64_t *)&tv, 2);
719 723 #else
720 724 adr_int32(&adr, (int32_t *)&tv, 2);
721 725 #endif
722 726 adr_short(&adr, &i, 1);
723 727 adr_char(&adr, &c, 1);
724 728
725 729 if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) !=
726 730 adr_count(&adr)) {
727 731 if (when == f_start) {
728 732 (void) sprintf(errbuf,
729 733 gettext("%s error writing header to %s. "),
730 734 ar,
731 735 f_outfile ? f_outfile :
732 736 gettext("standard output"));
733 737 } else {
734 738 (void) sprintf(errbuf,
735 739 gettext("%s error writing trailer to %s. "),
736 740 ar,
737 741 f_outfile ? f_outfile :
738 742 gettext("standard output"));
739 743 }
740 744 perror(errbuf);
741 745 return (-1);
742 746 }
743 747 return (0);
744 748 }
745 749
746 750
747 751 /*
748 752 * .func write_trailer - write audit file trailer.
749 753 * .desc Write an audit file trailer to the output stream. The finish
750 754 * time for the trailer is the time of the last record written
751 755 * to the stream.
752 756 * .ret 0 - succesful write.
753 757 * .ret -1 - failed write - message printed.
754 758 */
755 759 static int
756 760 write_trailer(void)
757 761 {
758 762 return (write_file_token(f_end));
759 763 }
760 764
761 765
762 766 /*
763 767 * .func rename_outfile - rename the outfile.
764 768 * .desc If the user used the -O flag they only gave us the suffix name
765 769 * for the outfile. We have to add the time stamps to put the filename
766 770 * in the proper audit file name format. The start time will be the time
767 771 * of the first record in the file and the end time will be the time of
768 772 * the last record in the file.
769 773 * .ret 0 - rename succesful.
770 774 * .ret -1 - rename failed - message printed.
771 775 */
772 776 static int
773 777 rename_outfile(void)
774 778 {
775 779 char f_newfile[MAXFILELEN];
776 780 char buf1[15], buf2[15];
777 781 char *f_file, *f_nfile, *f_time, *f_name;
778 782
779 783 if (f_outfile != NULL) {
780 784 /*
781 785 * Get string representations of start and end times.
782 786 */
783 787 derive_str(f_start, buf1);
784 788 derive_str(f_end, buf2);
785 789
786 790 f_nfile = f_time = f_newfile; /* working copy */
787 791 f_file = f_name = f_outfile; /* their version */
788 792 while (*f_file) {
789 793 if (*f_file == '/') { /* look for filename */
790 794 f_time = f_nfile + 1;
791 795 f_name = f_file + 1;
792 796 }
793 797 *f_nfile++ = *f_file++; /* make copy of their version */
794 798 }
795 799 *f_time = '\0';
796 800 /* start time goes first */
797 801 (void) strcat(f_newfile, buf1);
798 802 (void) strcat(f_newfile, ".");
799 803 /* then the finish time */
800 804 (void) strcat(f_newfile, buf2);
801 805 (void) strcat(f_newfile, ".");
802 806 /* and the name they gave us */
803 807 (void) strcat(f_newfile, f_name);
804 808
805 809 #if AUDIT_FILE
806 810 (void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n",
807 811 f_outfile, f_newfile);
808 812 #endif
809 813
810 814 #if AUDIT_RENAME
811 815 if (rename(f_outtemp, f_newfile) == -1) {
812 816 (void) fprintf(stderr,
813 817 "%s rename of %s to %s failed.\n",
814 818 ar, f_outtemp, f_newfile);
815 819 return (-1);
816 820 }
817 821 f_outfile = f_newfile;
818 822 #else
819 823 if (rename(f_outtemp, f_outfile) == -1) {
820 824 (void) fprintf(stderr,
821 825 gettext("%s rename of %s to %s failed.\n"),
822 826 ar, f_outtemp, f_outfile);
823 827 return (-1);
824 828 }
825 829 #endif
826 830 }
827 831 return (0);
828 832 }
829 833
830 834
831 835 /*
832 836 * .func open_outfile - open the outfile.
833 837 * .desc Open the outfile specified by the -O option. Assign it to the
834 838 * the standard output. Get a unique temporary name to use so we
835 839 * don't clobber an existing file.
836 840 * .ret 0 - no errors detected.
837 841 * .ret -1 - errors in processing (message already printed).
838 842 */
839 843 static int
840 844 open_outfile(void)
841 845 {
842 846 int tmpfd = -1;
843 847
844 848 if (f_outfile != NULL) {
845 849 f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8);
846 850 (void) strcpy(f_outtemp, f_outfile);
847 851 (void) strcat(f_outtemp, "XXXXXX");
848 852 if ((tmpfd = mkstemp(f_outtemp)) == -1) {
849 853 (void) sprintf(errbuf,
850 854 gettext("%s couldn't create temporary file"), ar);
851 855 perror(errbuf);
852 856 return (-1);
853 857 }
854 858 (void) fflush(stdout);
855 859 if (tmpfd != fileno(stdout)) {
856 860 if ((dup2(tmpfd, fileno(stdout))) == -1) {
857 861 (void) sprintf(errbuf,
858 862 gettext("%s can't assign %s to the "
859 863 "standard output"), ar, f_outfile);
860 864 perror(errbuf);
861 865 return (-1);
862 866 }
863 867 (void) close(tmpfd);
864 868 }
865 869 }
866 870 return (0);
867 871 }
868 872
869 873
870 874 /*
871 875 * .func init_options - initialize the options.
872 876 * .desc Give initial and/or default values to some options.
873 877 * .call init_options();
874 878 * .arg none.
875 879 * .ret void.
876 880 */
877 881 static void
878 882 init_options(void)
879 883 {
880 884 struct timeval tp;
881 885 struct timezone tpz;
882 886
883 887 /*
884 888 * Get current time for general use.
885 889 */
886 890 if (gettimeofday(&tp, &tpz) == -1)
887 891 perror(gettext("auditreduce: initial getttimeofday failed"));
888 892
889 893 time_now = tp.tv_sec; /* save for general use */
890 894 f_start = 0; /* first record time default */
891 895 f_end = time_now; /* last record time default */
892 896 m_after = 0; /* Jan 1, 1970 00:00:00 */
893 897
894 898 /*
895 899 * Setup initial size of audit_pcbs[].
896 900 */
897 901 pcbsize = PCB_INITSIZE; /* initial size of file-holding pcb's */
898 902
899 903 audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t));
900 904
901 905 /* description of 'current' error */
902 906 error_str = gettext("initial error");
903 907
904 908 }
905 909
906 910
907 911 /*
908 912 * .func a_calloc - audit calloc.
909 913 * .desc Calloc with check for failure. This is called by all of the
910 914 * places that want memory.
911 915 * .call ptr = a_calloc(nelem, size).
912 916 * .arg nelem - number of elements to allocate.
913 917 * .arg size - size of each element.
914 918 * .ret ptr - ptr to allocated and zeroed memory.
915 919 * .ret never - if calloc fails then we never return.
916 920 */
917 921 void *
918 922 a_calloc(int nelem, size_t size)
919 923 {
920 924 void *ptr;
921 925
922 926 if ((ptr = calloc((unsigned)nelem, size)) == NULL) {
923 927 perror(gettext("auditreduce: memory allocation failed"));
924 928 exit(1);
925 929 }
926 930 return (ptr);
927 931 }
928 932
929 933
930 934 /*
931 935 * .func init_sig - initial signal catching.
932 936 *
933 937 * .desc
934 938 * Setup the signal catcher to catch the SIGCHLD signal plus
935 939 * "environmental" signals -- keyboard plus other externally
936 940 * generated signals such as out of file space or cpu time. If a
937 941 * child exits with either a non-zero exit code or was killed by
938 942 * a signal to it then we will also exit with a non-zero exit
939 943 * code. In this way abnormal conditions can be passed up to the
940 944 * root process and the entire run be halted. Also catch the int
941 945 * and quit signals. Remove the output file since it is in an
942 946 * inconsistent state.
943 947 * .call ret = init_sig().
944 948 * .arg none.
945 949 * .ret 0 - no errors detected.
946 950 * .ret -1 - signal failed (message printed).
947 951 */
948 952 static int
949 953 init_sig(void)
950 954 {
951 955 if (signal(SIGCHLD, chld_handler) == SIG_ERR) {
952 956 perror(gettext("auditreduce: SIGCHLD signal failed"));
953 957 return (-1);
954 958 }
955 959
956 960 if (signal(SIGHUP, int_handler) == SIG_ERR) {
957 961 perror(gettext("auditreduce: SIGHUP signal failed"));
958 962 return (-1);
959 963 }
960 964 if (signal(SIGINT, int_handler) == SIG_ERR) {
961 965 perror(gettext("auditreduce: SIGINT signal failed"));
962 966 return (-1);
963 967 }
964 968 if (signal(SIGQUIT, int_handler) == SIG_ERR) {
965 969 perror(gettext("auditreduce: SIGQUIT signal failed"));
966 970 return (-1);
967 971 }
968 972 if (signal(SIGABRT, int_handler) == SIG_ERR) {
969 973 perror(gettext("auditreduce: SIGABRT signal failed"));
970 974 return (-1);
971 975 }
972 976 if (signal(SIGTERM, int_handler) == SIG_ERR) {
973 977 perror(gettext("auditreduce: SIGTERM signal failed"));
974 978 return (-1);
975 979 }
976 980 if (signal(SIGPWR, int_handler) == SIG_ERR) {
977 981 perror(gettext("auditreduce: SIGPWR signal failed"));
978 982 return (-1);
979 983 }
980 984 if (signal(SIGXCPU, int_handler) == SIG_ERR) {
981 985 perror(gettext("auditreduce: SIGXCPU signal failed"));
982 986 return (-1);
983 987 }
984 988 if (signal(SIGXFSZ, int_handler) == SIG_ERR) {
985 989 perror(gettext("auditreduce: SIGXFSZ signal failed"));
986 990 return (-1);
987 991 }
988 992 if (signal(SIGSEGV, int_handler) == SIG_ERR) {
989 993 perror(gettext("auditreduce: SIGSEGV signal failed"));
990 994 return (-1);
991 995 }
992 996
993 997 return (0);
994 998 }
995 999
996 1000
997 1001 /*
998 1002 * .func chld_handler - handle child signals.
999 1003 * .desc Catch the SIGCHLD signals. Remove the root process
1000 1004 * output file because it is in an inconsistent state.
1001 1005 * Print a message giving the signal number and/or return code
1002 1006 * of the child who caused the signal.
1003 1007 * .ret void.
1004 1008 */
1005 1009 /* ARGSUSED */
1006 1010 void
1007 1011 chld_handler(int sig)
1008 1012 {
1009 1013 int pid;
1010 1014 int status;
1011 1015
1012 1016 /*
1013 1017 * Get pid and reasons for cause of event.
1014 1018 */
1015 1019 pid = wait(&status);
1016 1020
1017 1021 if (pid > 0) {
1018 1022 /*
1019 1023 * If child received a signal or exited with a non-zero
1020 1024 * exit status then print message and exit
1021 1025 */
1022 1026 if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) ||
1023 1027 (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) {
1024 1028 (void) fprintf(stderr,
1025 1029 gettext("%s abnormal child termination - "), ar);
1026 1030
1027 1031 if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) {
1028 1032 psignal(WLOBYTE(status), "signal");
1029 1033 if (WCOREDUMP(status))
1030 1034 (void) fprintf(stderr,
1031 1035 gettext("core dumped\n"));
1032 1036 }
1033 1037
1034 1038 if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)
1035 1039 (void) fprintf(stderr, gettext(
1036 1040 "return code %d\n"),
1037 1041 WHIBYTE(status));
1038 1042
1039 1043 /*
1040 1044 * Get rid of outfile - it is suspect.
1041 1045 */
1042 1046 if (f_outfile != NULL) {
1043 1047 (void) close_outfile();
1044 1048 rm_outfile();
1045 1049 }
1046 1050 /*
1047 1051 * Give statistical info that may be useful.
1048 1052 */
1049 1053 audit_stats();
1050 1054
1051 1055 exit(1);
1052 1056 }
1053 1057 }
1054 1058 }
1055 1059
1056 1060
1057 1061 /*
1058 1062 * .func int_handler - handle quit/int signals.
1059 1063 * .desc Catch the keyboard and other environmental signals.
1060 1064 * Remove the root process output file because it is in
1061 1065 * an inconsistent state.
1062 1066 * .ret void.
1063 1067 */
1064 1068 /* ARGSUSED */
1065 1069 void
1066 1070 int_handler(int sig)
1067 1071 {
1068 1072 if (getpid() == root_pid) {
1069 1073 (void) close_outfile();
1070 1074 rm_outfile();
1071 1075 exit(1);
1072 1076 }
1073 1077 /*
1074 1078 * For a child process don't give an error exit or the
1075 1079 * parent process will catch it with the chld_handler and
1076 1080 * try to erase the outfile again.
1077 1081 */
1078 1082 exit(0);
1079 1083 }
|
↓ open down ↓ |
986 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX