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