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