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