1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * convert binary audit records to syslog messages and 26 * send them off to syslog 27 * 28 */ 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 /* 32 * auditd_plugin_open(), auditd_plugin() and auditd_plugin_close() 33 * implement a replacable library for use by auditd; they are a 34 * project private interface and may change without notice. 35 * 36 */ 37 #define DEBUG 0 38 #if DEBUG 39 #define DPRINT(x) {fprintf x; } 40 #else 41 #define DPRINT(x) 42 #endif 43 44 #include <arpa/inet.h> 45 #include <assert.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <grp.h> 49 #include <libintl.h> 50 #include <netdb.h> 51 #include <netinet/in.h> 52 #include <pthread.h> 53 #include <pwd.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <time.h> 58 #include <syslog.h> 59 #include <sys/types.h> 60 #include <sys/socket.h> 61 #include <unistd.h> 62 63 #include <bsm/audit.h> 64 #include <bsm/audit_record.h> 65 #include <security/auditd.h> 66 67 #include "toktable.h" 68 #include "sysplugin.h" 69 #include "systoken.h" 70 #include <audit_plugin.h> 71 72 #if DEBUG 73 static FILE *dbfp; /* debug file */ 74 #endif 75 76 extern void init_tokens(); 77 extern int parse_token(parse_context_t *); 78 79 static au_mask_t mask; 80 static int initialized = 0; 81 static size_t maxavail; 82 static pthread_mutex_t log_mutex; 83 84 #define ELLIPSIS "..." 85 #define ELLIPSIS_SIZE (sizeof (ELLIPSIS) - 1) 86 87 /* 88 * simple hashing for uid and hostname lookup 89 * 90 * performance tests showed that cacheing the hostname, uid, and gid 91 * make about a 40% difference for short audit records and regularly 92 * repeating hostname, uid, etc 93 * 94 * ht_type and ht_ip are only used for hostname lookup cacheing. 95 */ 96 typedef struct hashtable { 97 uint32_t ht_key; 98 uint32_t ht_type; 99 uint32_t ht_ip[4]; 100 char *ht_value; 101 size_t ht_length; 102 } hashtable_t; 103 #define HOSTHASHSIZE 128 104 #define UIDHASHSIZE 128 105 #define GIDHASHSIZE 32 106 107 static hashtable_t uidhash[UIDHASHSIZE]; 108 static hashtable_t gidhash[GIDHASHSIZE]; 109 static hashtable_t hosthash[HOSTHASHSIZE]; 110 111 #define STRCONSTARGS(s) (s), (sizeof (s) - 1) 112 /* 113 * the hash "handles" collisions by overwriting the old 114 * hash entry with the new. Perfection is not the goal. 115 * 116 * the key (s) is a 32 bit integer, handled here as 117 * four bytes. If the hash size is increased beyond 118 * 256, this macro will need some work. 119 */ 120 #define HASH(s, r, m) {\ 121 uint32_t _mush = 0;\ 122 int _i;\ 123 for (_i = 0; _i < 4; _i++) {\ 124 _mush ^= *(s)++;\ 125 }\ 126 r = _mush % m;\ 127 } 128 129 130 /* 131 * The default mask for sysplugin is to reject all record types. 132 * The parameters input here select which classes to allow. 133 * 134 * getauditflgsbin() outputs error messages to syslog. 135 * 136 * caller must hold log_mutex 137 */ 138 139 static auditd_rc_t 140 setmask(const char *flags) 141 { 142 au_mask_t tmask; 143 char *input, *ip, c; 144 auditd_rc_t rc = AUDITD_SUCCESS; 145 146 mask.am_success = 0x0; 147 mask.am_failure = 0x0; 148 149 if (flags != NULL) { 150 /* 151 * getauditflagsbin doesn't like blanks, but admins do 152 */ 153 input = malloc(strlen(flags) + 1); 154 if (input == NULL) 155 return (AUDITD_NO_MEMORY); 156 157 ip = input; 158 159 for (; (c = *flags) != '\0'; flags++) { 160 if (c == ' ') 161 continue; 162 *ip++ = c; 163 } 164 *ip = '\0'; 165 if (getauditflagsbin(input, &tmask) == 0) { 166 mask.am_success |= tmask.am_success; 167 mask.am_failure |= tmask.am_failure; 168 } 169 } 170 if ((mask.am_success | mask.am_failure) == 0) { 171 rc = AUDITD_INVALID; 172 __audit_syslog("audit_syslog.so", LOG_CONS | LOG_NDELAY, 173 LOG_DAEMON, LOG_ERR, 174 gettext("plugin is configured with empty class mask\n")); 175 } 176 free(input); 177 return (rc); 178 } 179 180 /* 181 * based on the current value of mask, either keep or toss the 182 * current audit record. The input is 1 for success, -1 for 183 * failure. 0 means no exit or return token was seen. 184 * 185 * au_preselect returns 1 for keep it, 0 for delete it, and 186 * -1 for some sort of error. Here, 1 and -1 are considered 187 * equivalent. tossit() returns 1 for delete it and 0 for 188 * keep it. 189 */ 190 191 static int 192 tossit(au_event_t id, int passfail) 193 { 194 int rc; 195 int selFlag; 196 197 switch (passfail) { 198 case 1: 199 selFlag = AU_PRS_SUCCESS; 200 break; 201 case -1: 202 selFlag = AU_PRS_FAILURE; 203 break; 204 default: /* no exit or return token */ 205 selFlag = AU_PRS_BOTH; 206 break; 207 } 208 (void) pthread_mutex_lock(&log_mutex); 209 rc = au_preselect(id, &mask, selFlag, AU_PRS_USECACHE); 210 (void) pthread_mutex_unlock(&log_mutex); 211 212 return (rc == 0); 213 } 214 215 /* 216 * the three bytes for ellipsis could potentially be longer than the 217 * space available for text if maxavail is within two bytes of 218 * OUTPUT_BUF_SIZE, which can happen if the hostname is one or two 219 * characters long. If there isn't room for ellipsis, there isn't 220 * room for the data, so it is simply dropped. 221 */ 222 223 static size_t 224 fromleft(char *p, size_t avail, char *attrname, size_t attrlen, char *txt, 225 size_t txtlen) 226 { 227 size_t len; 228 229 if (avail < attrlen + ELLIPSIS_SIZE) 230 return (0); 231 232 (void) memcpy(p, attrname, attrlen); 233 p += attrlen; 234 avail -= attrlen; 235 if (txtlen > avail) { 236 (void) memcpy(p, ELLIPSIS, ELLIPSIS_SIZE); 237 txt += txtlen - (avail - ELLIPSIS_SIZE); 238 (void) memcpy(p + ELLIPSIS_SIZE, txt, avail - ELLIPSIS_SIZE); 239 len = attrlen + avail; 240 p += avail; 241 } else { 242 (void) memcpy(p, txt, txtlen); 243 len = attrlen + txtlen; 244 p += txtlen; 245 } 246 *p = '\0'; 247 return (len); 248 } 249 250 static size_t 251 fromright(char *p, size_t avail, char *attrname, size_t attrlen, char *txt, 252 size_t txtlen) 253 { 254 size_t len; 255 256 if (avail < attrlen + ELLIPSIS_SIZE) 257 return (0); 258 259 (void) memcpy(p, attrname, attrlen); 260 p += attrlen; 261 avail -= attrlen; 262 if (txtlen > avail) { 263 (void) memcpy(p, txt, avail - ELLIPSIS_SIZE); 264 (void) memcpy(p + (avail - ELLIPSIS_SIZE), 265 ELLIPSIS, ELLIPSIS_SIZE); 266 len = attrlen + avail; 267 p += avail; 268 } else { 269 (void) memcpy(p, txt, txtlen); 270 p += txtlen; 271 len = attrlen + txtlen; 272 } 273 *p = '\0'; 274 return (len); 275 } 276 277 static int 278 init_hash(hashtable_t *table, int bad_key, int table_length, 279 size_t max_value) 280 { 281 int i; 282 283 for (i = 0; i < table_length; i++) { 284 table[i].ht_value = malloc(max_value + 1); 285 table[i].ht_key = bad_key; 286 table[i].ht_length = 0; 287 if (table[i].ht_value == NULL) { 288 int j; 289 for (j = 0; j < i; j++) 290 free(table[j].ht_value); 291 return (-1); 292 } 293 *(table[i].ht_value) = '\0'; 294 } 295 return (0); 296 } 297 298 static void 299 free_hash(hashtable_t *table, int table_length) 300 { 301 int i; 302 303 for (i = 0; i < table_length; i++) { 304 free(table[i].ht_value); 305 } 306 } 307 308 309 /* 310 * do IP -> hostname lookup 311 */ 312 #define UNKNOWN "unknown" 313 #define UNKNOWN_LEN (sizeof (UNKNOWN)) 314 315 static size_t 316 gethname(au_tid_addr_t *tid, char *p, size_t max, char *prefix, 317 size_t prefix_len) 318 { 319 size_t len, l; 320 struct hostent *host; 321 int rc; 322 int af; 323 int ix; 324 char *hash_key; 325 uint32_t key; 326 int match; 327 328 if (prefix_len > max) 329 return (0); 330 331 (void) memcpy(p, prefix, prefix_len); 332 p += prefix_len; 333 max -= prefix_len; 334 335 if (tid->at_type == AU_IPv6) { 336 key = tid->at_addr[0] ^ 337 tid->at_addr[1] ^ 338 tid->at_addr[2] ^ 339 tid->at_addr[3]; 340 } else 341 key = (tid->at_addr[0]); 342 343 hash_key = (char *)&key; 344 345 HASH(hash_key, ix, HOSTHASHSIZE); 346 347 match = 0; 348 349 if (key == 0) { 350 l = UNKNOWN_LEN; /* includes end of string */ 351 if (l > max) 352 l = max; 353 len = prefix_len + strlcpy(p, UNKNOWN, l); 354 return (len); 355 } 356 357 if (tid->at_type == AU_IPv6) { 358 if ((key == hosthash[ix].ht_key) && 359 (hosthash[ix].ht_type == tid->at_type)) { 360 int i; 361 match = 1; 362 for (i = 0; i < 4; i++) { 363 if (hosthash[ix].ht_ip[i] != tid->at_addr[i]) { 364 match = 0; 365 break; 366 } 367 } 368 } 369 } else if (key == hosthash[ix].ht_key) { 370 match = 1; 371 } 372 if (!match) { 373 hosthash[ix].ht_key = key; 374 hosthash[ix].ht_type = tid->at_type; 375 376 if (tid->at_type == AU_IPv4) { 377 hosthash[ix].ht_ip[0] = tid->at_addr[0]; 378 af = AF_INET; 379 } else { 380 (void) memcpy((char *)hosthash[ix].ht_ip, 381 (char *)tid->at_addr, AU_IPv6); 382 af = AF_INET6; 383 } 384 host = getipnodebyaddr((const void *)tid->at_addr, 385 tid->at_type, af, &rc); 386 387 if (host == NULL) { 388 (void) inet_ntop(af, (void *)tid->at_addr, 389 hosthash[ix].ht_value, MAXHOSTNAMELEN); 390 hosthash[ix].ht_length = strlen(hosthash[ix].ht_value); 391 } else { 392 hosthash[ix].ht_length = strlcpy(hosthash[ix].ht_value, 393 host->h_name, MAXHOSTNAMELEN); 394 freehostent(host); 395 } 396 } 397 l = hosthash[ix].ht_length + 1; 398 if (l > max) 399 l = max; 400 401 len = prefix_len + strlcpy(p, hosthash[ix].ht_value, l); 402 403 return (len); 404 } 405 /* 406 * the appropriate buffer length for getpwuid_r() isn't documented; 407 * 1024 should be enough. 408 */ 409 #define GETPWUID_BUFF_LEN 1024 410 #define USERNAMELEN 256 411 #define GIDNAMELEN 256 412 413 static size_t 414 getuname(uid_t uid, gid_t gid, char *p, size_t max, char *prefix, 415 size_t prefix_len) 416 { 417 struct passwd pw; 418 char pw_buf[GETPWUID_BUFF_LEN]; 419 size_t len, l; 420 struct group gr; 421 int ix; 422 char *hash_key; 423 424 if (prefix_len > max) 425 return (0); 426 427 len = prefix_len; 428 429 (void) memcpy(p, prefix, len); 430 p += len; 431 max -= len; 432 433 hash_key = (char *)&uid; 434 435 HASH(hash_key, ix, UIDHASHSIZE); 436 437 if (uid != uidhash[ix].ht_key) { 438 uidhash[ix].ht_key = uid; 439 440 if ((getpwuid_r(uid, &pw, pw_buf, GETPWUID_BUFF_LEN)) == NULL) 441 l = snprintf(uidhash[ix].ht_value, USERNAMELEN, 442 "%d", uid); 443 else 444 l = strlcpy(uidhash[ix].ht_value, pw.pw_name, 445 USERNAMELEN); 446 447 uidhash[ix].ht_length = l; 448 } 449 l = uidhash[ix].ht_length + 1; 450 if (l > max) 451 l = max; 452 (void) memcpy(p, uidhash[ix].ht_value, l); 453 len += l - 1; 454 455 if (gid != -2) { 456 p += l - 1; 457 max -= l - 1; 458 if (max < 2) 459 return (len); 460 461 hash_key = (char *)&gid; 462 HASH(hash_key, ix, GIDHASHSIZE); 463 464 if (gid != gidhash[ix].ht_key) { 465 gidhash[ix].ht_key = gid; 466 467 if (getgrgid_r(gid, &gr, pw_buf, GETPWUID_BUFF_LEN) == 468 NULL) 469 gidhash[ix].ht_length = 470 snprintf(gidhash[ix].ht_value, GIDNAMELEN, 471 "%d", gid); 472 else 473 gidhash[ix].ht_length = 474 strlcpy(gidhash[ix].ht_value, 475 gr.gr_name, GIDNAMELEN); 476 } 477 *p++ = ':'; 478 len++; 479 max--; 480 481 l = gidhash[ix].ht_length + 1; 482 if (l > max) 483 l = max; 484 (void) memcpy(p, gidhash[ix].ht_value, l); 485 len += l - 1; 486 } 487 return (len); 488 } 489 490 /* 491 * filter() parse input; toss if not wanted. 492 * 493 * the input value sequence is a number generated when the buffer 494 * was queued. ctx.out.sf_sequence, if not -1, is the sequence number 495 * generated in c2audit. It is not part of the "official" syslog 496 * output but is included if DEBUG is on. 497 */ 498 #define EVENT_NAME_LEN 32 499 500 static auditd_rc_t 501 filter(const char *input, uint32_t sequence, char *output, 502 size_t in_len, size_t out_len) 503 { 504 parse_context_t ctx; 505 char *bp; 506 auditd_rc_t rc = AUDITD_SUCCESS; 507 auditd_rc_t rc_ret = AUDITD_SUCCESS; 508 size_t used, remaining; 509 char *last_adr; /* infinite loop check */ 510 int token_count = 0; 511 int parse_rc; 512 513 static parse_context_t initial_ctx; 514 static int first = 1; 515 516 if (first) { 517 first = 0; 518 519 /* 520 * Any member or submember of parse_context_t which utilizes 521 * allocated memory must free() the memory after calling 522 * parse_token() for both the preselected and non-preselected 523 * cases. 524 * New additions to parse_context_t or its submembers need to 525 * have this same treatment. 526 */ 527 initial_ctx.out.sf_eventid = 0; 528 initial_ctx.out.sf_reclen = 0; 529 initial_ctx.out.sf_pass = 0; 530 initial_ctx.out.sf_asid = 0; 531 initial_ctx.out.sf_auid = -2; 532 initial_ctx.out.sf_euid = -2; 533 initial_ctx.out.sf_egid = -2; 534 initial_ctx.out.sf_tid.at_type = 0; 535 initial_ctx.out.sf_pauid = -2; 536 initial_ctx.out.sf_peuid = -2; 537 initial_ctx.out.sf_uauthlen = 0; 538 initial_ctx.out.sf_uauth = NULL; 539 initial_ctx.out.sf_pathlen = 0; 540 initial_ctx.out.sf_path = NULL; 541 initial_ctx.out.sf_atpathlen = 0; 542 initial_ctx.out.sf_atpath = NULL; 543 initial_ctx.out.sf_textlen = 0; 544 initial_ctx.out.sf_text = NULL; 545 initial_ctx.out.sf_sequence = -1; 546 initial_ctx.out.sf_zonelen = 0; 547 initial_ctx.out.sf_zonename = NULL; 548 549 init_tokens(); /* cmd/praudit/toktable.c */ 550 } 551 (void) memcpy(&ctx, &initial_ctx, sizeof (parse_context_t)); 552 ctx.id = sequence; 553 ctx.adr.adr_stream = (char *)input; 554 ctx.adr.adr_now = (char *)input; 555 556 last_adr = NULL; 557 while ((ctx.adr.adr_now - ctx.adr.adr_stream) < in_len) { 558 assert(last_adr != ctx.adr.adr_now); 559 token_count++; 560 last_adr = ctx.adr.adr_now; 561 if ((parse_rc = parse_token(&ctx)) != 0) { 562 char message[256]; 563 au_event_ent_t *event; 564 char event_name[EVENT_NAME_LEN]; 565 char sequence_str[EVENT_NAME_LEN]; 566 567 if (cacheauevent(&event, ctx.out.sf_eventid) < 0) 568 (void) snprintf(event_name, EVENT_NAME_LEN, 569 "%d", ctx.out.sf_eventid); 570 else 571 (void) strlcpy(event_name, event->ae_desc, 572 EVENT_NAME_LEN); 573 574 if (token_count < 2) 575 /* leave rc_ret unchanged */ 576 rc = AUDITD_INVALID; 577 578 if (ctx.out.sf_sequence != -1) 579 (void) snprintf(sequence_str, EVENT_NAME_LEN, 580 " (seq=%u) ", ctx.out.sf_sequence); 581 else 582 sequence_str[0] = '\0'; 583 584 (void) snprintf(message, 256, 585 gettext("error before token %d (previous token=%d)" 586 " of record type %s%s\n"), 587 token_count, parse_rc, event_name, sequence_str); 588 589 DPRINT((dbfp, message)); 590 591 __audit_syslog("audit_syslog.so", 592 LOG_PID | LOG_ODELAY | LOG_CONS, 593 LOG_DAEMON, LOG_ALERT, message); 594 break; 595 } 596 } 597 if (rc == AUDITD_SUCCESS) { 598 if (tossit(ctx.out.sf_eventid, ctx.out.sf_pass)) { 599 #if DEBUG 600 if (ctx.out.sf_sequence != -1) 601 fprintf(dbfp, 602 "syslog tossed (event=%d) record %u " 603 "/ buffer %u\n", 604 ctx.out.sf_eventid, ctx.out.sf_sequence, 605 sequence); 606 else 607 fprintf(dbfp, 608 "syslog tossed (event=%d) buffer %u\n", 609 ctx.out.sf_eventid, sequence); 610 #endif 611 612 /* 613 * Members or submembers of parse_context_t which 614 * utilize allocated memory need to free() the memory 615 * here to handle the case of not being preselected as 616 * well as below for when the event is preselected. 617 * New additions to parse_context_t or any of its 618 * submembers need to get the same treatment. 619 */ 620 if (ctx.out.sf_uauthlen > 0) { 621 free(ctx.out.sf_uauth); 622 ctx.out.sf_uauth = NULL; 623 ctx.out.sf_uauthlen = 0; 624 } 625 if (ctx.out.sf_pathlen > 0) { 626 free(ctx.out.sf_path); 627 ctx.out.sf_path = NULL; 628 ctx.out.sf_pathlen = 0; 629 } 630 if (ctx.out.sf_atpathlen > 0) { 631 free(ctx.out.sf_atpath); 632 ctx.out.sf_atpath = NULL; 633 ctx.out.sf_atpathlen = 0; 634 } 635 if (ctx.out.sf_textlen > 0) { 636 free(ctx.out.sf_text); 637 ctx.out.sf_text = NULL; 638 ctx.out.sf_textlen = 0; 639 } 640 if (ctx.out.sf_zonelen > 0) { 641 free(ctx.out.sf_zonename); 642 ctx.out.sf_zonename = NULL; 643 ctx.out.sf_zonelen = 0; 644 } 645 646 return (-1); /* tell caller it was tossed */ 647 } 648 bp = output; 649 remaining = out_len; 650 651 if (ctx.out.sf_eventid != 0) { 652 au_event_ent_t *event; 653 654 if (cacheauevent(&event, ctx.out.sf_eventid) < 0) 655 used = snprintf(bp, remaining, "%d", 656 ctx.out.sf_eventid); 657 else 658 used = strlcpy(bp, event->ae_desc, remaining); 659 bp += used; 660 remaining -= used; 661 } 662 if (ctx.out.sf_pass != 0) { 663 if (ctx.out.sf_pass < 0) 664 used = strlcpy(bp, " failed", remaining); 665 else 666 used = strlcpy(bp, " ok", remaining); 667 bp += used; 668 remaining -= used; 669 } 670 if (ctx.out.sf_asid != 0) { 671 used = snprintf(bp, remaining, " session %u", 672 ctx.out.sf_asid); 673 remaining -= used; 674 bp += used; 675 } 676 if (ctx.out.sf_auid != -2) { 677 used = getuname(ctx.out.sf_auid, -2, bp, remaining, 678 STRCONSTARGS(" by ")); 679 bp += used; 680 remaining -= used; 681 } 682 if (ctx.out.sf_euid != -2) { 683 /* 4 = strlen(" as ") */ 684 used = getuname(ctx.out.sf_euid, ctx.out.sf_egid, bp, 685 remaining, STRCONSTARGS(" as ")); 686 bp += used; 687 remaining -= used; 688 } 689 if (ctx.out.sf_zonename != NULL) { 690 used = fromright(bp, remaining, 691 STRCONSTARGS(" in "), 692 ctx.out.sf_zonename, ctx.out.sf_zonelen); 693 free(ctx.out.sf_zonename); 694 bp += used; 695 remaining -= used; 696 } 697 if (ctx.out.sf_tid.at_type != 0) { 698 /* 6 = strlen(" from ") */ 699 used = gethname(&(ctx.out.sf_tid), bp, remaining, 700 STRCONSTARGS(" from ")); 701 bp += used; 702 remaining -= used; 703 } 704 if (ctx.out.sf_pauid != -2) { 705 /* 11 = strlen(" proc_auid ") */ 706 used = getuname(ctx.out.sf_pauid, -2, bp, remaining, 707 STRCONSTARGS(" proc_auid ")); 708 bp += used; 709 remaining -= used; 710 } 711 if (ctx.out.sf_peuid != -2) { 712 used = getuname(ctx.out.sf_peuid, -2, bp, remaining, 713 STRCONSTARGS(" proc_uid ")); 714 bp += used; 715 remaining -= used; 716 } 717 #if DEBUG 718 /* 719 * with performance testing, this has the effect of 720 * making that each message is unique, so syslogd 721 * won't collect a series of messages as "last message 722 * repeated n times," another reason why DEBUG 0 723 * should perform better than DEBUG 1. However the 724 * intention is to help debug lost data problems 725 */ 726 if (ctx.out.sf_sequence != -1) { 727 fprintf(dbfp, 728 "syslog writing record %u / buffer %u\n", 729 ctx.out.sf_sequence, sequence); 730 used = snprintf(bp, remaining, " seq %u", 731 ctx.out.sf_sequence, sequence); 732 remaining -= used; 733 bp += used; 734 } else 735 fprintf(dbfp, "syslog writing buffer %u\n", sequence); 736 #endif 737 /* 738 * Long fields that may need truncation go here in 739 * order of decreasing priority. Paths are truncated 740 * from the left, text from the right. 741 */ 742 if (ctx.out.sf_path != NULL) { 743 used = fromleft(bp, remaining, STRCONSTARGS(" obj "), 744 ctx.out.sf_path, ctx.out.sf_pathlen); 745 free(ctx.out.sf_path); 746 bp += used; 747 remaining -= used; 748 } 749 if (ctx.out.sf_atpath != NULL) { 750 used = fromleft(bp, remaining, 751 STRCONSTARGS(" attr_obj "), 752 ctx.out.sf_atpath, ctx.out.sf_atpathlen); 753 free(ctx.out.sf_atpath); 754 bp += used; 755 remaining -= used; 756 } 757 if (ctx.out.sf_uauth != NULL) { 758 used = fromright(bp, remaining, STRCONSTARGS(" uauth "), 759 ctx.out.sf_uauth, ctx.out.sf_uauthlen); 760 free(ctx.out.sf_path); 761 bp += used; 762 remaining -= used; 763 } 764 if (ctx.out.sf_text != NULL) { 765 used = fromright(bp, remaining, 766 STRCONSTARGS(AU_TEXT_NAME), 767 ctx.out.sf_text, ctx.out.sf_textlen); 768 free(ctx.out.sf_text); 769 bp += used; 770 remaining -= used; 771 } 772 } 773 return (rc_ret); 774 } 775 776 /* 777 * 1024 is max syslog record size, 48 is minimum header length, 778 * assuming a hostname length of 0. maxavail reduces use of the 779 * allocated space by the length of the hostname (see maxavail) 780 */ 781 #define OUTPUT_BUF_SIZE 1024 - 48 782 783 /* ARGSUSED */ 784 auditd_rc_t 785 auditd_plugin(const char *input, size_t in_len, uint32_t sequence, char **error) 786 { 787 char *outbuf; 788 auditd_rc_t rc = AUDITD_SUCCESS; 789 #if DEBUG 790 static uint32_t last_sequence = 0; 791 static uint32_t write_count = 0; 792 static uint32_t toss_count = 0; 793 794 if ((last_sequence > 0) && (sequence != last_sequence + 1)) 795 fprintf(dbfp, "syslog: buffer sequence=%d but prev=%d\n", 796 sequence, last_sequence); 797 last_sequence = sequence; 798 #endif 799 800 *error = NULL; 801 802 outbuf = malloc(OUTPUT_BUF_SIZE); 803 if (outbuf == NULL) { 804 DPRINT((dbfp, "syslog: out of memory; seq=%u\n", 805 sequence)); 806 rc = AUDITD_NO_MEMORY; 807 *error = strdup(gettext("Can't allocate buffers")); 808 } else { 809 rc = filter(input, sequence, outbuf, in_len, maxavail); 810 811 if (rc == AUDITD_SUCCESS) { 812 __audit_syslog("audit", LOG_NDELAY, 813 LOG_AUDIT, LOG_NOTICE, outbuf); 814 DPRINT((dbfp, "syslog: write_count=%u, " 815 "buffer=%u, tossed=%d\n", 816 ++write_count, sequence, toss_count)); 817 } else if (rc > 0) { /* -1 == discard it */ 818 DPRINT((dbfp, "syslog: parse failed for buffer %u\n", 819 sequence)); 820 *error = strdup(gettext( 821 "Unable to parse audit record")); 822 } else { 823 DPRINT((dbfp, "syslog: rc = %d (-1 is discard), " 824 "sequence=%u, toss_count=%d\n", 825 rc, sequence, ++toss_count)); 826 rc = 0; 827 } 828 free(outbuf); 829 } 830 return (rc); 831 } 832 833 auditd_rc_t 834 auditd_plugin_open(const kva_t *kvlist, char **ret_list, char **error) 835 { 836 char localname[MAXHOSTNAMELEN + 1]; 837 auditd_rc_t rc; 838 char *value; 839 /* kva_match doesn't do const, so copy the pointer */ 840 kva_t *kva = (kva_t *)kvlist; 841 842 *error = NULL; 843 *ret_list = NULL; 844 845 if ((kvlist == NULL) || ((value = kva_match(kva, "p_flags")) == NULL)) { 846 *error = strdup(gettext( 847 "The \"p_flags\" attribute is missing.")); 848 return (AUDITD_INVALID); 849 } 850 if (!initialized) { 851 #if DEBUG 852 dbfp = __auditd_debug_file_open(); 853 #endif 854 initialized = 1; 855 (void) pthread_mutex_init(&log_mutex, NULL); 856 /* 857 * calculate length of the local hostname for adjusting the 858 * estimate of how much space is taken by the syslog header. 859 * If the local hostname isn't available, leave some room 860 * anyway. (The -2 is for the blanks on either side of the 861 * hostname in the syslog message.) 862 */ 863 (void) pthread_mutex_lock(&log_mutex); 864 if (gethostname(localname, MAXHOSTNAMELEN)) 865 maxavail = OUTPUT_BUF_SIZE - 20; 866 else 867 maxavail = OUTPUT_BUF_SIZE - strlen(localname) - 2; 868 (void) pthread_mutex_unlock(&log_mutex); 869 870 if (init_hash(hosthash, 0, HOSTHASHSIZE, MAXHOSTNAMELEN)) 871 return (AUDITD_NO_MEMORY); 872 873 if (init_hash(uidhash, -2, UIDHASHSIZE, USERNAMELEN)) 874 return (AUDITD_NO_MEMORY); 875 876 if (init_hash(gidhash, -2, GIDHASHSIZE, GIDNAMELEN)) 877 return (AUDITD_NO_MEMORY); 878 } 879 (void) pthread_mutex_lock(&log_mutex); 880 if ((rc = setmask(value)) != AUDITD_SUCCESS) 881 *error = strdup(gettext( 882 "incorrect p_flags setting; no records will be output")); 883 884 (void) pthread_mutex_unlock(&log_mutex); 885 886 return (rc); 887 } 888 889 auditd_rc_t 890 auditd_plugin_close(char **error) 891 { 892 *error = NULL; 893 894 if (initialized) { 895 (void) pthread_mutex_destroy(&log_mutex); 896 897 free_hash(hosthash, HOSTHASHSIZE); 898 free_hash(uidhash, UIDHASHSIZE); 899 free_hash(gidhash, GIDHASHSIZE); 900 } 901 initialized = 0; 902 903 return (AUDITD_SUCCESS); 904 } 905