Print this page
6139 help gcc figure out variable initialization
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/auditd_plugins/binfile/binfile.c
+++ new/usr/src/lib/auditd_plugins/binfile/binfile.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.
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 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23 23 *
24 24 * write binary audit records directly to a file.
25 25 */
26 26
27 27 #define DEBUG 0
28 28
29 29 #if DEBUG
30 30 #define DPRINT(x) { (void) fprintf x; }
31 31 #else
32 32 #define DPRINT(x)
33 33 #endif
34 34
35 35 /*
36 36 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close()
37 37 * implement a replacable library for use by auditd; they are a
38 38 * project private interface and may change without notice.
39 39 *
40 40 */
41 41
42 42 #include <assert.h>
43 43 #include <bsm/audit.h>
44 44 #include <bsm/audit_record.h>
45 45 #include <bsm/libbsm.h>
46 46 #include <errno.h>
47 47 #include <fcntl.h>
48 48 #include <libintl.h>
49 49 #include <netdb.h>
50 50 #include <pthread.h>
51 51 #include <secdb.h>
52 52 #include <signal.h>
53 53 #include <stdio.h>
54 54 #include <stdlib.h>
55 55 #include <string.h>
56 56 #include <sys/param.h>
57 57 #include <sys/types.h>
58 58 #include <time.h>
59 59 #include <tzfile.h>
60 60 #include <unistd.h>
61 61 #include <sys/vfs.h>
62 62 #include <limits.h>
63 63 #include <syslog.h>
64 64 #include <security/auditd.h>
65 65 #include <audit_plugin.h>
66 66
67 67 #define AUDIT_DATE_SZ 14
68 68 #define AUDIT_FNAME_SZ 2 * AUDIT_DATE_SZ + 2 + MAXHOSTNAMELEN
69 69
70 70 /* per-directory status */
71 71 #define SOFT_SPACE 0 /* minfree or less space available */
72 72 #define PLENTY_SPACE 1 /* more than minfree available */
73 73 #define SPACE_FULL 2 /* out of space */
74 74
75 75 #define AVAIL_MIN 50 /* If there are less that this number */
76 76 /* of blocks avail, the filesystem is */
77 77 /* presumed full. */
78 78
79 79 #define ALLHARD_DELAY 20 /* Call audit_warn(allhard) every 20 seconds */
80 80
81 81 /* minimum reasonable size in bytes to roll over an audit file */
82 82 #define FSIZE_MIN 512000
83 83
84 84 /*
85 85 * The directory list is a circular linked list. It is pointed into by
86 86 * activeDir. Each element contains the pointer to the next
87 87 * element, the directory pathname, a flag for how much space there is
88 88 * in the directory's filesystem, and a file handle. Since a new
89 89 * directory list can be created from auditd_plugin_open() while the
90 90 * current list is in use, activeDir is protected by log_mutex.
91 91 */
92 92 typedef struct dirlist_s dirlist_t;
93 93 struct dirlist_s {
94 94 dirlist_t *dl_next;
95 95 int dl_space;
96 96 int dl_flags;
97 97 char *dl_dirname;
98 98 char *dl_filename; /* file name (not path) if open */
99 99 int dl_fd; /* file handle, -1 unless open */
100 100 };
101 101 /*
102 102 * Defines for dl_flags
103 103 */
104 104 #define SOFT_WARNED 0x0001 /* already did soft warning for this dir */
105 105 #define HARD_WARNED 0x0002 /* already did hard warning for this dir */
106 106
107 107 #if DEBUG
108 108 static FILE *dbfp; /* debug file */
109 109 #endif
110 110
111 111 static pthread_mutex_t log_mutex;
112 112 static int binfile_is_open = 0;
113 113
114 114 static int minfree = -1;
115 115 static int minfreeblocks; /* minfree in blocks */
116 116
117 117 static dirlist_t *lastOpenDir = NULL; /* last activeDir */
118 118 static dirlist_t *activeDir = NULL; /* to be current directory */
119 119 static dirlist_t *startdir; /* first dir in the ring */
120 120 static int activeCount = 0; /* number of dirs in the ring */
121 121
122 122 static int openNewFile = 0; /* need to open a new file */
123 123 static int hung_count = 0; /* count of audit_warn hard */
124 124
125 125 /* flag from audit_plugin_open to audit_plugin_close */
126 126 static int am_open = 0;
127 127 /* preferred dir state */
128 128 static int fullness_state = PLENTY_SPACE;
129 129
130 130 /*
131 131 * These are used to implement a maximum size for the auditing
132 132 * file. binfile_maxsize is set via the 'p_fsize' parameter to the
133 133 * audit_binfile plugin.
134 134 */
135 135 static uint_t binfile_cursize = 0;
136 136 static uint_t binfile_maxsize = 0;
137 137
138 138 static int open_log(dirlist_t *);
139 139
140 140 static void
141 141 freedirlist(dirlist_t *head)
142 142 {
143 143 dirlist_t *n1, *n2;
144 144 /*
145 145 * Free up the old directory list if any
146 146 */
147 147 if (head != NULL) {
148 148 n1 = head;
149 149 do {
150 150 n2 = n1->dl_next;
151 151 free(n1->dl_dirname);
152 152 free(n1->dl_filename);
153 153 free(n1);
154 154 n1 = n2;
155 155 } while (n1 != head);
156 156 }
157 157 }
158 158
159 159 dirlist_t *
160 160 dupdirnode(dirlist_t *node_orig)
161 161 {
162 162 dirlist_t *node_new;
163 163
164 164 if ((node_new = calloc(1, sizeof (dirlist_t))) == NULL) {
165 165 return (NULL);
166 166 }
167 167
168 168 if (node_orig->dl_dirname != NULL &&
169 169 (node_new->dl_dirname = strdup(node_orig->dl_dirname)) == NULL ||
170 170 node_orig->dl_filename != NULL &&
171 171 (node_new->dl_filename = strdup(node_orig->dl_filename)) == NULL) {
172 172 freedirlist(node_new);
173 173 return (NULL);
174 174 }
175 175
176 176 node_new->dl_next = node_new;
177 177 node_new->dl_space = node_orig->dl_space;
178 178 node_new->dl_flags = node_orig->dl_flags;
179 179 node_new->dl_fd = node_orig->dl_fd;
180 180
181 181 return (node_new);
182 182 }
183 183
184 184 /*
185 185 * add to a linked list of directories available for writing
186 186 *
187 187 */
188 188 static int
189 189 growauditlist(dirlist_t **listhead, char *dirlist,
190 190 dirlist_t *endnode, int *count)
191 191 {
192 192 dirlist_t *node;
193 193 char *bs, *be;
194 194 dirlist_t **node_p;
195 195 char *dirname;
196 196 char *remainder;
197 197
198 198 DPRINT((dbfp, "binfile: dirlist=%s\n", dirlist));
199 199
200 200 if (*listhead == NULL)
201 201 node_p = listhead;
202 202 else
203 203 node_p = &(endnode->dl_next);
204 204
205 205 node = NULL;
206 206 while ((dirname = strtok_r(dirlist, ",", &remainder)) != NULL) {
207 207 dirlist = NULL;
208 208
209 209 DPRINT((dbfp, "binfile: p_dir = %s\n", dirname));
210 210
211 211 (*count)++;
212 212 node = malloc(sizeof (dirlist_t));
213 213 if (node == NULL)
214 214 return (AUDITD_NO_MEMORY);
215 215
216 216 node->dl_flags = 0;
217 217 node->dl_filename = NULL;
218 218 node->dl_fd = -1;
219 219 node->dl_space = PLENTY_SPACE;
220 220
221 221 node->dl_dirname = malloc((unsigned)strlen(dirname) + 1);
222 222 if (node->dl_dirname == NULL)
223 223 return (AUDITD_NO_MEMORY);
224 224
225 225 bs = dirname;
226 226 while ((*bs == ' ') || (*bs == '\t')) /* trim blanks */
227 227 bs++;
228 228 be = bs + strlen(bs) - 1;
229 229 while (be > bs) { /* trim trailing blanks */
230 230 if ((*bs != ' ') && (*bs != '\t'))
231 231 break;
232 232 be--;
233 233 }
234 234 *(be + 1) = '\0';
235 235 (void) strlcpy(node->dl_dirname, bs, AUDIT_FNAME_SZ);
236 236
237 237 if (*listhead != NULL)
238 238 node->dl_next = *listhead;
239 239 else
240 240 node->dl_next = node;
241 241 *node_p = node;
242 242 node_p = &(node->dl_next);
243 243
244 244 }
245 245 return (0);
246 246 }
247 247
248 248 /*
249 249 * create a linked list of directories available for writing
250 250 *
251 251 * if a list already exists, the two are compared and the new one is
252 252 * used only if it is different than the old.
253 253 *
254 254 * returns -2 for new or changed list, 0 for unchanged list and -1 for
255 255 * error. (Positive returns are for AUDITD_<error code> values)
256 256 *
257 257 */
258 258 static int
259 259 loadauditlist(char *dirstr, char *minfreestr)
260 260 {
261 261 dirlist_t *n1, *n2;
262 262 dirlist_t *listhead = NULL;
263 263 dirlist_t *thisdir;
264 264 int node_count = 0;
265 265 int rc;
266 266 int temp_minfree;
267 267
268 268 static dirlist_t *activeList = NULL; /* directory list */
269 269
270 270 DPRINT((dbfp, "binfile: Loading audit list from audit service "
271 271 "(audit_binfile)\n"));
272 272
273 273 if (dirstr == NULL || minfreestr == NULL) {
274 274 DPRINT((dbfp, "binfile: internal error"));
275 275 return (-1);
276 276 }
277 277 if ((rc = growauditlist(&listhead, dirstr, NULL, &node_count)) != 0) {
278 278 return (rc);
279 279 }
280 280 if (node_count == 0) {
281 281 /*
282 282 * there was a problem getting the directory
283 283 * list or remote host info from the audit_binfile
284 284 * configuration even though auditd thought there was
285 285 * at least 1 good entry
286 286 */
287 287 DPRINT((dbfp, "binfile: "
288 288 "problem getting directory / libpath list "
289 289 "from audit_binfile configuration.\n"));
290 290 return (-1);
291 291 }
292 292
293 293 #if DEBUG
294 294 /* print out directory list */
295 295 if (listhead != NULL) {
296 296 (void) fprintf(dbfp, "Directory list:\n\t%s\n",
297 297 listhead->dl_dirname);
298 298 thisdir = listhead->dl_next;
299 299
300 300 while (thisdir != listhead) {
301 301 (void) fprintf(dbfp, "\t%s\n", thisdir->dl_dirname);
302 302 thisdir = thisdir->dl_next;
303 303 }
304 304 }
305 305 #endif /* DEBUG */
306 306
307 307 thisdir = listhead;
308 308
309 309 /* See if the list has changed (rc = 0 if no change, else 1) */
310 310 rc = 0;
311 311 if (node_count == activeCount) {
312 312 n1 = listhead;
313 313 n2 = activeList;
314 314 do {
315 315 if (strcmp(n1->dl_dirname, n2->dl_dirname) != 0) {
316 316 DPRINT((dbfp,
317 317 "binfile: new dirname = %s\n"
318 318 "binfile: old dirname = %s\n",
319 319 n1->dl_dirname,
320 320 n2->dl_dirname));
321 321 rc = -2;
322 322 break;
323 323 }
324 324 n1 = n1->dl_next;
325 325 n2 = n2->dl_next;
326 326 } while ((n1 != listhead) && (n2 != activeList));
327 327 } else {
328 328 DPRINT((dbfp, "binfile: dir counts differs\n"
329 329 "binfile: old dir count = %d\n"
330 330 "binfile: new dir count = %d\n",
331 331 activeCount, node_count));
332 332 rc = -2;
333 333 }
334 334 if (rc == -2) {
335 335 (void) pthread_mutex_lock(&log_mutex);
336 336 DPRINT((dbfp, "loadauditlist: close / open audit.log(4)\n"));
337 337 if (open_log(listhead) == 0) {
338 338 openNewFile = 1; /* try again later */
339 339 } else {
340 340 openNewFile = 0;
341 341 }
↓ open down ↓ |
341 lines elided |
↑ open up ↑ |
342 342 freedirlist(activeList); /* old list */
343 343 activeList = listhead; /* new list */
344 344 activeDir = startdir = thisdir;
345 345 activeCount = node_count;
346 346 (void) pthread_mutex_unlock(&log_mutex);
347 347 } else {
348 348 freedirlist(listhead);
349 349 }
350 350
351 351 /* Get the minfree value. */
352 - if (minfreestr != NULL)
353 - temp_minfree = atoi(minfreestr);
352 + temp_minfree = atoi(minfreestr);
354 353
355 354 if ((temp_minfree < 0) || (temp_minfree > 100))
356 355 temp_minfree = 0;
357 356
358 357 if (minfree != temp_minfree) {
359 358 DPRINT((dbfp, "minfree: old = %d, new = %d\n",
360 359 minfree, temp_minfree));
361 360 rc = -2; /* data change */
362 361 minfree = temp_minfree;
363 362 }
364 363
365 364 return (rc);
366 365 }
367 366
368 367 /*
369 368 * getauditdate - get the current time (GMT) and put it in the form
370 369 * yyyymmddHHMMSS .
371 370 */
372 371 static void
373 372 getauditdate(char *date)
374 373 {
375 374 struct timeval tp;
376 375 struct timezone tzp;
377 376 struct tm tm;
378 377
379 378 (void) gettimeofday(&tp, &tzp);
380 379 tm = *gmtime(&tp.tv_sec);
381 380 /*
382 381 * NOTE: if we want to use gmtime, we have to be aware that the
383 382 * structure only keeps the year as an offset from TM_YEAR_BASE.
384 383 * I have used TM_YEAR_BASE in this code so that if they change
385 384 * this base from 1900 to 2000, it will hopefully mean that this
386 385 * code does not have to change. TM_YEAR_BASE is defined in
387 386 * tzfile.h .
388 387 */
389 388 (void) sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d",
390 389 tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday,
391 390 tm.tm_hour, tm.tm_min, tm.tm_sec);
392 391 }
393 392
394 393
395 394
396 395 /*
397 396 * write_file_token - put the file token into the audit log
398 397 */
399 398 static int
400 399 write_file_token(int fd, char *name)
401 400 {
402 401 adr_t adr; /* xdr ptr */
403 402 struct timeval tv; /* time now */
404 403 char for_adr[AUDIT_FNAME_SZ + AUDIT_FNAME_SZ]; /* plenty of room */
405 404 char token_id;
406 405 short i;
407 406
408 407 (void) gettimeofday(&tv, (struct timezone *)0);
409 408 i = strlen(name) + 1;
410 409 adr_start(&adr, for_adr);
411 410 #ifdef _LP64
412 411 token_id = AUT_OTHER_FILE64;
413 412 adr_char(&adr, &token_id, 1);
414 413 adr_int64(&adr, (int64_t *)& tv, 2);
415 414 #else
416 415 token_id = AUT_OTHER_FILE32;
417 416 adr_char(&adr, &token_id, 1);
418 417 adr_int32(&adr, (int32_t *)& tv, 2);
419 418 #endif
420 419
421 420 adr_short(&adr, &i, 1);
422 421 adr_char(&adr, name, i);
423 422
424 423 if (write(fd, for_adr, adr_count(&adr)) < 0) {
425 424 DPRINT((dbfp, "binfile: Bad write\n"));
426 425 return (errno);
427 426 }
428 427 return (0);
429 428 }
430 429
431 430 /*
432 431 * close_log - close the file if open. Also put the name of the
433 432 * new log file in the trailer, and rename the old file
434 433 * to oldname. The caller must hold log_mutext while calling
435 434 * close_log since any change to activeDir is a complete redo
436 435 * of all it points to.
437 436 * arguments -
438 437 * oldname - the new name for the file to be closed
439 438 * newname - the name of the new log file (for the trailer)
440 439 */
441 440 static void
442 441 close_log(dirlist_t **lastOpenDir_ptr, char *oname, char *newname)
443 442 {
444 443 char auditdate[AUDIT_DATE_SZ+1];
445 444 char *name;
446 445 char oldname[AUDIT_FNAME_SZ+1];
447 446 dirlist_t *currentdir = *lastOpenDir_ptr;
448 447
449 448 if ((currentdir == NULL) || (currentdir->dl_fd == -1))
450 449 return;
451 450 /*
452 451 * If oldname is blank, we were called by auditd_plugin_close()
453 452 * instead of by open_log, so we need to update our name.
454 453 */
455 454 (void) strlcpy(oldname, oname, AUDIT_FNAME_SZ);
456 455
457 456 if (strcmp(oldname, "") == 0) {
458 457 getauditdate(auditdate);
459 458
460 459 assert(currentdir->dl_filename != NULL);
461 460
462 461 (void) strlcpy(oldname, currentdir->dl_filename,
463 462 AUDIT_FNAME_SZ);
464 463
465 464 name = strrchr(oldname, '/') + 1;
466 465 (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
467 466 AUDIT_DATE_SZ);
468 467 }
469 468 /*
470 469 * Write the trailer record and rename and close the file.
471 470 * If any of the write, rename, or close fail, ignore it
472 471 * since there is not much else we can do and the next open()
473 472 * will trigger the necessary full directory logic.
474 473 *
475 474 * newname is "" if binfile is being closed down.
476 475 */
477 476 (void) write_file_token(currentdir->dl_fd, newname);
478 477 if (currentdir->dl_fd >= 0) {
479 478 (void) fsync(currentdir->dl_fd);
480 479 (void) close(currentdir->dl_fd);
481 480 }
482 481 currentdir->dl_fd = -1;
483 482 (void) rename(currentdir->dl_filename, oldname);
484 483
485 484 DPRINT((dbfp, "binfile: Log closed %s\n", oldname));
486 485
487 486 freedirlist(currentdir);
488 487 *lastOpenDir_ptr = NULL;
489 488 }
490 489
491 490
492 491 /*
493 492 * open_log - open a new file in the current directory. If a
494 493 * file is already open, close it.
495 494 *
496 495 * return 1 if ok, 0 if all directories are full.
497 496 *
498 497 * lastOpenDir - used to get the oldfile name (and change it),
499 498 * to close the oldfile.
500 499 *
501 500 * The caller must hold log_mutex while calling open_log.
502 501 *
503 502 */
504 503 static int
505 504 open_log(dirlist_t *current_dir)
506 505 {
507 506 char auditdate[AUDIT_DATE_SZ + 1];
508 507 char oldname[AUDIT_FNAME_SZ + 1] = "";
509 508 char newname[AUDIT_FNAME_SZ + 1];
510 509 char *name; /* pointer into oldname */
511 510 int opened = 0;
512 511 int error = 0;
513 512 int newfd = 0;
514 513
515 514 static char host[MAXHOSTNAMELEN + 1] = "";
516 515 /* previous directory with open log file */
517 516
518 517 if (host[0] == '\0')
519 518 (void) gethostname(host, MAXHOSTNAMELEN);
520 519
521 520 /* Get a filename which does not already exist */
522 521 while (!opened) {
523 522 getauditdate(auditdate);
524 523 (void) snprintf(newname, AUDIT_FNAME_SZ,
525 524 "%s/%s.not_terminated.%s",
526 525 current_dir->dl_dirname, auditdate, host);
527 526 newfd = open(newname,
528 527 O_RDWR | O_APPEND | O_CREAT | O_EXCL, 0640);
529 528 if (newfd < 0) {
530 529 switch (errno) {
531 530 case EEXIST:
532 531 DPRINT((dbfp,
533 532 "open_log says duplicate for %s "
534 533 "(will try another)\n", newname));
535 534 (void) sleep(1);
536 535 break;
537 536 case ENOENT: {
538 537 /* invalid path */
539 538 char *msg;
540 539 (void) asprintf(&msg,
541 540 gettext("No such p_dir: %s\n"),
542 541 current_dir->dl_dirname);
543 542 DPRINT((dbfp,
544 543 "open_log says about %s: %s\n",
545 544 newname, strerror(errno)));
546 545 __audit_syslog("audit_binfile.so",
547 546 LOG_CONS | LOG_NDELAY,
548 547 LOG_DAEMON, LOG_ERR, msg);
549 548 free(msg);
550 549 current_dir = current_dir->dl_next;
551 550 return (0);
552 551 }
553 552 default:
554 553 /* open failed */
555 554 DPRINT((dbfp,
556 555 "open_log says full for %s: %s\n",
557 556 newname, strerror(errno)));
558 557 current_dir->dl_space = SPACE_FULL;
559 558 current_dir = current_dir->dl_next;
560 559 return (0);
561 560 } /* switch */
562 561 } else
563 562 opened = 1;
564 563 } /* while */
565 564
566 565 /*
567 566 * When we get here, we have opened our new log file.
568 567 * Now we need to update the name of the old file to
569 568 * store in this file's header. lastOpenDir may point
570 569 * to current_dir if the list is only one entry long and
571 570 * there is only one list.
572 571 */
573 572 if ((lastOpenDir != NULL) && (lastOpenDir->dl_filename != NULL)) {
574 573 (void) strlcpy(oldname, lastOpenDir->dl_filename,
575 574 AUDIT_FNAME_SZ);
576 575 name = (char *)strrchr(oldname, '/') + 1;
577 576
578 577 (void) memcpy(name + AUDIT_DATE_SZ + 1, auditdate,
579 578 AUDIT_DATE_SZ);
580 579
581 580 close_log(&lastOpenDir, oldname, newname);
582 581 }
583 582 error = write_file_token(newfd, oldname);
584 583 if (error) {
585 584 /* write token failed */
586 585 (void) close(newfd);
587 586
588 587 current_dir->dl_space = SPACE_FULL;
589 588 current_dir->dl_fd = -1;
590 589 free(current_dir->dl_filename);
591 590 current_dir->dl_filename = NULL;
592 591 current_dir = current_dir->dl_next;
593 592 return (0);
594 593 } else {
595 594 if (current_dir->dl_filename != NULL) {
596 595 free(current_dir->dl_filename);
597 596 }
598 597 current_dir->dl_filename = strdup(newname);
599 598 current_dir->dl_fd = newfd;
600 599
601 600 if (lastOpenDir == NULL) {
602 601 freedirlist(lastOpenDir);
603 602 if ((lastOpenDir = dupdirnode(current_dir)) == NULL) {
604 603 __audit_syslog("audit_binfile.so",
605 604 LOG_CONS | LOG_NDELAY,
606 605 LOG_DAEMON, LOG_ERR, gettext("no memory"));
607 606 return (0);
608 607 }
609 608 DPRINT((dbfp, "open_log created new lastOpenDir "
610 609 "(%s, %s [fd: %d])\n",
611 610 lastOpenDir->dl_dirname == NULL ? "" :
612 611 lastOpenDir->dl_dirname,
613 612 lastOpenDir->dl_filename == NULL ? "" :
614 613 lastOpenDir->dl_filename, lastOpenDir->dl_fd));
615 614 }
616 615
617 616 /*
618 617 * New file opened, so reset file size statistic (used
619 618 * to ensure audit log does not grow above size limit
620 619 * set by p_fsize).
621 620 */
622 621 binfile_cursize = 0;
623 622
624 623 (void) __logpost(newname);
625 624
626 625 DPRINT((dbfp, "binfile: Log opened: %s\n", newname));
627 626 return (1);
628 627 }
629 628 }
630 629
631 630 #define IGNORE_SIZE 8192
632 631 /*
633 632 * spacecheck - determine whether the given directory's filesystem
634 633 * has the at least the space requested. Also set the space
635 634 * value in the directory list structure. If the caller
636 635 * passes other than PLENTY_SPACE or SOFT_SPACE, the caller should
637 636 * ignore the return value. Otherwise, 0 = less than the
638 637 * requested space is available, 1 = at least the requested space
639 638 * is available.
640 639 *
641 640 * log_mutex must be held by the caller
642 641 *
643 642 * -1 is returned if stat fails
644 643 *
645 644 * IGNORE_SIZE is one page (Sol 9 / 10 timeframe) and is the default
646 645 * buffer size written for Sol 9 and earlier. To keep the same accuracy
647 646 * for the soft limit check as before, spacecheck checks for space
648 647 * remaining IGNORE_SIZE bytes. This reduces the number of statvfs()
649 648 * calls and related math.
650 649 *
651 650 * globals -
652 651 * minfree - the soft limit, i.e., the % of filesystem to reserve
653 652 */
654 653 static int
655 654 spacecheck(dirlist_t *thisdir, int test_limit, size_t next_buf_size)
656 655 {
657 656 struct statvfs sb;
658 657 static int ignore_size = 0;
659 658
660 659 ignore_size += next_buf_size;
661 660
662 661 if ((test_limit == PLENTY_SPACE) && (ignore_size < IGNORE_SIZE))
663 662 return (1);
664 663
665 664 assert(thisdir != NULL);
666 665
667 666 if (statvfs(thisdir->dl_dirname, &sb) < 0) {
668 667 thisdir->dl_space = SPACE_FULL;
669 668 minfreeblocks = AVAIL_MIN;
670 669 return (-1);
671 670 } else {
672 671 minfreeblocks = ((minfree * sb.f_blocks) / 100) + AVAIL_MIN;
673 672
674 673 if (sb.f_bavail < AVAIL_MIN)
675 674 thisdir->dl_space = SPACE_FULL;
676 675 else if (sb.f_bavail > minfreeblocks) {
677 676 thisdir->dl_space = fullness_state = PLENTY_SPACE;
678 677 ignore_size = 0;
679 678 } else
680 679 thisdir->dl_space = SOFT_SPACE;
681 680 }
682 681 if (thisdir->dl_space == PLENTY_SPACE)
683 682 return (1);
684 683
685 684 return (thisdir->dl_space == test_limit);
686 685 }
687 686
688 687 /*
689 688 * Parses p_fsize value and contains it within the range FSIZE_MIN and
690 689 * INT_MAX so using uints won't cause an undetected overflow of
691 690 * INT_MAX. Defaults to 0 if the value is invalid or is missing.
692 691 */
693 692 static void
694 693 save_maxsize(char *maxsize) {
695 694 /*
696 695 * strtol() returns a long which could be larger than int so
697 696 * store here for sanity checking first
698 697 */
699 698 long proposed_maxsize;
700 699
701 700 if (maxsize != NULL) {
702 701 /*
703 702 * There is no explicit error return from strtol() so
704 703 * we may need to depend on the value of errno.
705 704 */
706 705 errno = 0;
707 706 proposed_maxsize = strtol(maxsize, (char **)NULL, 10);
708 707
709 708 /*
710 709 * If sizeof(long) is greater than sizeof(int) on this
711 710 * platform, proposed_maxsize might be greater than
712 711 * INT_MAX without it being reported as ERANGE.
713 712 */
714 713 if ((errno == ERANGE) ||
715 714 ((proposed_maxsize != 0) &&
716 715 (proposed_maxsize < FSIZE_MIN)) ||
717 716 (proposed_maxsize > INT_MAX)) {
718 717 binfile_maxsize = 0;
719 718 DPRINT((dbfp, "binfile: p_fsize parameter out of "
720 719 "range: %s\n", maxsize));
721 720 /*
722 721 * Inform administrator of the error via
723 722 * syslog
724 723 */
725 724 __audit_syslog("audit_binfile.so",
726 725 LOG_CONS | LOG_NDELAY,
727 726 LOG_DAEMON, LOG_ERR,
728 727 gettext("p_fsize parameter out of range\n"));
729 728 } else {
730 729 binfile_maxsize = proposed_maxsize;
731 730 }
732 731 } else { /* p_fsize string was not present */
733 732 binfile_maxsize = 0;
734 733 }
735 734
736 735 DPRINT((dbfp, "binfile: set maxsize to %d\n", binfile_maxsize));
737 736 }
738 737
739 738 /*
740 739 * auditd_plugin() writes a buffer to the currently open file. The
741 740 * global "openNewFile" is used to force a new log file for cases such
742 741 * as the initial open, when minfree is reached, the p_fsize value is
743 742 * exceeded or the current file system fills up, and "audit -s" with
744 743 * changed parameters. For "audit -n" a new log file is opened
745 744 * immediately in auditd_plugin_open().
746 745 *
747 746 * This function manages one or more audit directories as follows:
748 747 *
749 748 * If the current open file is in a directory that has not
750 749 * reached the soft limit, write the input data and return.
751 750 *
752 751 * Scan the list of directories for one which has not reached
753 752 * the soft limit; if one is found, write and return. Such
754 753 * a writable directory is in "PLENTY_SPACE" state.
755 754 *
756 755 * Scan the list of directories for one which has not reached
757 756 * the hard limit; if one is found, write and return. This
758 757 * directory in in "SOFT_SPACE" state.
759 758 *
760 759 * Oh, and if a write fails, handle it like a hard space limit.
761 760 *
762 761 * audit_warn (via __audit_dowarn()) is used to alert an operator
763 762 * at various levels of fullness.
764 763 */
765 764 /* ARGSUSED */
766 765 auditd_rc_t
767 766 auditd_plugin(const char *input, size_t in_len, uint64_t sequence, char **error)
768 767 {
769 768 auditd_rc_t rc = AUDITD_FAIL;
770 769 int open_status;
771 770 size_t out_len;
772 771 /* avoid excess audit_warnage */
773 772 static int allsoftfull_warning = 0;
774 773 static int allhard_pause = 0;
775 774 static struct timeval next_allhard;
776 775 struct timeval now;
777 776 #if DEBUG
778 777 int statrc;
779 778 static char *last_file_written_to = NULL;
780 779 static uint64_t last_sequence = 0;
781 780 static uint64_t write_count = 0;
782 781
783 782 if ((last_sequence > 0) && (sequence != last_sequence + 1))
784 783 (void) fprintf(dbfp,
785 784 "binfile: buffer sequence=%llu but prev=%llu=n",
786 785 sequence, last_sequence);
787 786 last_sequence = sequence;
788 787
789 788 (void) fprintf(dbfp, "binfile: input seq=%llu, len=%d\n",
790 789 sequence, in_len);
791 790 #endif
792 791 *error = NULL;
793 792 /*
794 793 * lock is for activeDir, referenced by open_log() and close_log()
795 794 */
796 795 (void) pthread_mutex_lock(&log_mutex);
797 796
798 797 /*
799 798 * If this would take us over the maximum size, open a new
800 799 * file, unless maxsize is 0, in which case growth of the
801 800 * audit log is unrestricted.
802 801 */
803 802 if ((binfile_maxsize != 0) &&
804 803 ((binfile_cursize + in_len) > binfile_maxsize)) {
805 804 DPRINT((dbfp, "binfile: maxsize exceeded, opening new audit "
806 805 "file.\n"));
807 806 openNewFile = 1;
808 807 }
809 808
810 809 while (rc == AUDITD_FAIL) {
811 810 open_status = 1;
812 811 if (openNewFile) {
813 812 open_status = open_log(activeDir);
814 813 if (open_status == 1) /* ok */
815 814 openNewFile = 0;
816 815 }
817 816 /*
818 817 * consider "space ok" return and error return the same;
819 818 * a -1 means spacecheck couldn't check for space.
820 819 */
821 820 #if !DEBUG
822 821 if ((open_status == 1) &&
823 822 (spacecheck(activeDir, fullness_state, in_len) != 0)) {
824 823 #else
825 824 if ((open_status == 1) &&
826 825 (statrc = spacecheck(activeDir, fullness_state,
827 826 in_len) != 0)) {
828 827 DPRINT((dbfp, "binfile: returned from spacecheck\n"));
829 828 /*
830 829 * The last copy of last_file_written_to is
831 830 * never free'd, so there will be one open
832 831 * memory reference on exit. It's debug only.
833 832 */
834 833 if ((last_file_written_to != NULL) &&
835 834 (strcmp(last_file_written_to,
836 835 activeDir->dl_filename) != 0)) {
837 836 DPRINT((dbfp, "binfile: now writing to %s\n",
838 837 activeDir->dl_filename));
839 838 free(last_file_written_to);
840 839 }
841 840 DPRINT((dbfp, "binfile: finished some debug stuff\n"));
842 841 last_file_written_to =
843 842 strdup(activeDir->dl_filename);
844 843 #endif
845 844 out_len = write(activeDir->dl_fd, input, in_len);
846 845 DPRINT((dbfp, "binfile: finished the write\n"));
847 846
848 847 binfile_cursize += out_len;
849 848
850 849 if (out_len == in_len) {
851 850 DPRINT((dbfp,
852 851 "binfile: write_count=%llu, sequence=%llu,"
853 852 " l=%u\n",
854 853 ++write_count, sequence, out_len));
855 854 allsoftfull_warning = 0;
856 855 activeDir->dl_flags = 0;
857 856
858 857 rc = AUDITD_SUCCESS;
859 858 break;
860 859 } else if (!(activeDir->dl_flags & HARD_WARNED)) {
861 860 DPRINT((dbfp,
862 861 "binfile: write failed, sequence=%llu, "
863 862 "l=%u\n", sequence, out_len));
864 863 DPRINT((dbfp, "hard warning sent.\n"));
865 864 __audit_dowarn("hard", activeDir->dl_dirname,
866 865 0);
867 866
868 867 activeDir->dl_flags |= HARD_WARNED;
869 868 }
870 869 } else {
871 870 DPRINT((dbfp, "binfile: statrc=%d, fullness_state=%d\n",
872 871 statrc, fullness_state));
873 872 if (!(activeDir->dl_flags & SOFT_WARNED) &&
874 873 (activeDir->dl_space == SOFT_SPACE)) {
875 874 DPRINT((dbfp, "soft warning sent\n"));
876 875 __audit_dowarn("soft",
877 876 activeDir->dl_dirname, 0);
878 877 activeDir->dl_flags |= SOFT_WARNED;
879 878 }
880 879 if (!(activeDir->dl_flags & HARD_WARNED) &&
881 880 (activeDir->dl_space == SPACE_FULL)) {
882 881 DPRINT((dbfp, "hard warning sent.\n"));
883 882 __audit_dowarn("hard",
884 883 activeDir->dl_dirname, 0);
885 884 activeDir->dl_flags |= HARD_WARNED;
886 885 }
887 886 }
888 887 DPRINT((dbfp, "binfile: activeDir=%s, next=%s\n",
889 888 activeDir->dl_dirname, activeDir->dl_next->dl_dirname));
890 889
891 890 activeDir = activeDir->dl_next;
892 891 openNewFile = 1;
893 892
894 893 if (activeDir == startdir) { /* full circle */
895 894 if (fullness_state == PLENTY_SPACE) { /* once */
896 895 fullness_state = SOFT_SPACE;
897 896 if (allsoftfull_warning == 0) {
898 897 allsoftfull_warning++;
899 898 __audit_dowarn("allsoft", "", 0);
900 899 }
901 900 } else { /* full circle twice */
902 901 if ((hung_count > 0) && !allhard_pause) {
903 902 allhard_pause = 1;
904 903 (void) gettimeofday(&next_allhard,
905 904 NULL);
906 905 next_allhard.tv_sec += ALLHARD_DELAY;
907 906 }
908 907
909 908 if (allhard_pause) {
910 909 (void) gettimeofday(&now, NULL);
911 910 if (now.tv_sec >= next_allhard.tv_sec) {
912 911 allhard_pause = 0;
913 912 __audit_dowarn("allhard", "",
914 913 ++hung_count);
915 914 }
916 915 } else {
917 916 __audit_dowarn("allhard", "",
918 917 ++hung_count);
919 918 }
920 919 minfreeblocks = AVAIL_MIN;
921 920 rc = AUDITD_RETRY;
922 921 *error = strdup(gettext(
923 922 "all partitions full\n"));
924 923 (void) __logpost("");
925 924 }
926 925 }
927 926 }
928 927 (void) pthread_mutex_unlock(&log_mutex);
929 928
930 929 return (rc);
931 930 }
932 931
933 932
934 933 /*
935 934 * It may be called multiple times as auditd handles SIGHUP and SIGUSR1
936 935 * corresponding to the audit(1M) flags -s and -n
937 936 *
938 937 * kvlist is NULL only if auditd caught a SIGUSR1 (audit -n), so after the first
939 938 * time open is called; the reason is -s if kvlist != NULL and -n otherwise.
940 939 *
941 940 */
942 941 auditd_rc_t
943 942 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error)
944 943 {
945 944 int rc = 0;
946 945 int status;
947 946 int reason;
948 947 char *dirlist;
949 948 char *minfree;
950 949 char *maxsize;
951 950 kva_t *kv;
952 951
953 952 *error = NULL;
954 953 *ret_list = NULL;
955 954 kv = (kva_t *)kvlist;
956 955
957 956 if (am_open) {
958 957 if (kvlist == NULL)
959 958 reason = 1; /* audit -n */
960 959 else
961 960 reason = 2; /* audit -s */
962 961 } else {
963 962 reason = 0; /* initial open */
964 963 #if DEBUG
965 964 dbfp = __auditd_debug_file_open();
966 965 #endif
967 966 }
968 967 DPRINT((dbfp, "binfile: am_open=%d, reason=%d\n", am_open, reason));
969 968
970 969 am_open = 1;
971 970
972 971 if (kvlist == NULL) {
973 972 dirlist = NULL;
974 973 minfree = NULL;
975 974 maxsize = NULL;
976 975 } else {
977 976 dirlist = kva_match(kv, "p_dir");
978 977 minfree = kva_match(kv, "p_minfree");
979 978 maxsize = kva_match(kv, "p_fsize");
980 979 }
981 980 switch (reason) {
982 981 case 0: /* initial open */
983 982 if (!binfile_is_open)
984 983 (void) pthread_mutex_init(&log_mutex, NULL);
985 984 binfile_is_open = 1;
986 985 openNewFile = 1;
987 986 /* FALLTHRU */
988 987 case 2: /* audit -s */
989 988 /* handle p_fsize parameter */
990 989 save_maxsize(maxsize);
991 990
992 991 fullness_state = PLENTY_SPACE;
993 992 status = loadauditlist(dirlist, minfree);
994 993
995 994 if (status == -1) {
996 995 (void) __logpost("");
997 996 *error = strdup(gettext("no directories configured"));
998 997 return (AUDITD_RETRY);
999 998 } else if (status == AUDITD_NO_MEMORY) {
1000 999 (void) __logpost("");
1001 1000 *error = strdup(gettext("no memory"));
1002 1001 return (status);
1003 1002 } else { /* status is 0 or -2 (no change or changed) */
1004 1003 hung_count = 0;
1005 1004 DPRINT((dbfp, "binfile: loadauditlist returned %d\n",
1006 1005 status));
1007 1006 }
1008 1007 break;
1009 1008 case 1: /* audit -n */
1010 1009 (void) pthread_mutex_lock(&log_mutex);
1011 1010 if (open_log(activeDir) == 1) /* ok */
1012 1011 openNewFile = 0;
1013 1012 (void) pthread_mutex_unlock(&log_mutex);
1014 1013 break;
1015 1014 }
1016 1015
1017 1016 rc = AUDITD_SUCCESS;
1018 1017 *ret_list = NULL;
1019 1018
1020 1019 return (rc);
1021 1020 }
1022 1021
1023 1022 auditd_rc_t
1024 1023 auditd_plugin_close(char **error)
1025 1024 {
1026 1025 *error = NULL;
1027 1026
1028 1027 (void) pthread_mutex_lock(&log_mutex);
1029 1028 close_log(&lastOpenDir, "", "");
1030 1029 freedirlist(activeDir);
1031 1030 activeDir = NULL;
1032 1031 (void) pthread_mutex_unlock(&log_mutex);
1033 1032
1034 1033 DPRINT((dbfp, "binfile: closed\n"));
1035 1034
1036 1035 (void) __logpost("");
1037 1036
1038 1037 if (binfile_is_open) {
1039 1038 (void) pthread_mutex_destroy(&log_mutex);
1040 1039 binfile_is_open = 0;
1041 1040 #if DEBUG
1042 1041 } else {
1043 1042 (void) fprintf(dbfp,
1044 1043 "auditd_plugin_close() called when already closed.");
1045 1044 #endif
1046 1045 }
1047 1046 am_open = 0;
1048 1047 return (AUDITD_SUCCESS);
1049 1048 }
↓ open down ↓ |
686 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX