1212793Sdim/*- 2212793Sdim * Copyright (c) 2006 Robert N. M. Watson 3212793Sdim * All rights reserved. 4212793Sdim * 5212793Sdim * This software was developed by Robert Watson for the TrustedBSD Project. 6212793Sdim * 7212793Sdim * Redistribution and use in source and binary forms, with or without 8212793Sdim * modification, are permitted provided that the following conditions 9212793Sdim * are met: 10212793Sdim * 1. Redistributions of source code must retain the above copyright 11212793Sdim * notice, this list of conditions and the following disclaimer. 12249423Sdim * 2. Redistributions in binary form must reproduce the above copyright 13249423Sdim * notice, this list of conditions and the following disclaimer in the 14249423Sdim * documentation and/or other materials provided with the distribution. 15249423Sdim * 16249423Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17212793Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18212793Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19212793Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20249423Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21212793Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22212793Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23212793Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24212793Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25212793Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26212793Sdim * SUCH DAMAGE. 27212793Sdim */ 28212793Sdim 29212793Sdim/* 30212793Sdim * Configuration file parser for auditfilterd. The configuration file is a 31212793Sdim * very simple format, similar to other BSM configuration files, consisting 32212793Sdim * of configuration entries of one line each. The configuration function is 33212793Sdim * aware of previous runs, and will update the current configuration as 34212793Sdim * needed. 35212793Sdim * 36212793Sdim * Modules are in one of two states: attached, or detached. If attach fails, 37212793Sdim * detach is not called because it was not attached. If a module is attached 38212793Sdim * and a call to its reinit method fails, we will detach it. 39212793Sdim * 40212793Sdim * Modules are passed a (void *) reference to their configuration state so 41212793Sdim * that they may pass this into any common APIs we provide which may rely on 42212793Sdim * that state. Currently, the only such API is the cookie API, which allows 43212793Sdim * per-instance state to be maintained by a module. In the future, this will 44212793Sdim * also be used to support per-instance preselection state. 45212793Sdim */ 46212793Sdim 47212793Sdim#include <sys/types.h> 48212793Sdim 49212793Sdim#include <config/config.h> 50212793Sdim#ifdef HAVE_FULL_QUEUE_H 51212793Sdim#include <sys/queue.h> 52212793Sdim#else 53212793Sdim#include <compat/queue.h> 54212793Sdim#endif 55212793Sdim 56212793Sdim#include <bsm/libbsm.h> 57212793Sdim#include <bsm/audit_filter.h> 58212793Sdim 59212793Sdim#include <dlfcn.h> 60212793Sdim#include <err.h> 61212793Sdim#include <errno.h> 62212793Sdim#include <limits.h> 63212793Sdim#include <stdio.h> 64212793Sdim#include <stdlib.h> 65212793Sdim#include <string.h> 66212793Sdim 67212793Sdim#include "auditfilterd.h" 68212793Sdim 69212793Sdim/* 70212793Sdim * Free an individual auditfilter_module structure. Will not shut down the 71212793Sdim * module, just frees the memory. Does so conditional on pointers being 72212793Sdim * non-NULL so that it can be used on partially allocated structures. 73221345Sdim */ 74221345Sdimstatic void 75221345Sdimauditfilter_module_free(struct auditfilter_module *am) 76221345Sdim{ 77221345Sdim 78221345Sdim if (am->am_modulename != NULL) 79221345Sdim free(am->am_modulename); 80221345Sdim if (am->am_arg_buffer != NULL) 81221345Sdim free(am->am_arg_buffer); 82221345Sdim if (am->am_argv != NULL) 83221345Sdim free(am->am_argv); 84221345Sdim} 85221345Sdim 86221345Sdim/* 87221345Sdim * Free all memory associated with an auditfilter_module list. Does not 88221345Sdim * dlclose() or shut down the modules, just free the memory. Use 89221345Sdim * auditfilter_module_list_detach() for that, if required. 90221345Sdim */ 91221345Sdimstatic void 92221345Sdimauditfilter_module_list_free(struct auditfilter_module_list *list) 93221345Sdim{ 94221345Sdim struct auditfilter_module *am; 95221345Sdim 96221345Sdim while (!(TAILQ_EMPTY(list))) { 97221345Sdim am = TAILQ_FIRST(list); 98221345Sdim TAILQ_REMOVE(list, am, am_list); 99212793Sdim auditfilter_module_free(am); 100212793Sdim } 101212793Sdim} 102212793Sdim 103212793Sdim/* 104212793Sdim * Detach an attached module from an auditfilter_module structure. Does not 105212793Sdim * free the data structure itself. 106212793Sdim */ 107212793Sdimstatic void 108212793Sdimauditfilter_module_detach(struct auditfilter_module *am) 109212793Sdim{ 110212793Sdim 111212793Sdim if (am->am_detach != NULL) 112212793Sdim am->am_detach(am); 113212793Sdim am->am_cookie = NULL; 114212793Sdim (void)dlclose(am->am_dlhandle); 115212793Sdim am->am_dlhandle = NULL; 116212793Sdim} 117212793Sdim 118212793Sdim/* 119212793Sdim * Walk an auditfilter_module list, detaching each module. Intended to be 120212793Sdim * combined with auditfilter_module_list_free(). 121212793Sdim */ 122212793Sdimstatic void 123212793Sdimauditfilter_module_list_detach(struct auditfilter_module_list *list) 124212793Sdim{ 125239462Sdim struct auditfilter_module *am; 126239462Sdim 127212793Sdim TAILQ_FOREACH(am, list, am_list) 128239462Sdim auditfilter_module_detach(am); 129212793Sdim} 130212793Sdim 131212793Sdim/* 132212793Sdim * Given a filled out auditfilter_module, use dlopen() and dlsym() to attach 133212793Sdim * the module. If we fail, leave fields in the state we found them. 134212793Sdim * 135212793Sdim * XXXRW: Need a better way to report errors. 136212793Sdim */ 137212793Sdimstatic int 138212793Sdimauditfilter_module_attach(struct auditfilter_module *am) 139212793Sdim{ 140212793Sdim 141212793Sdim am->am_dlhandle = dlopen(am->am_modulename, RTLD_NOW); 142212793Sdim if (am->am_dlhandle == NULL) { 143212793Sdim warnx("auditfilter_module_attach: %s: %s", am->am_modulename, 144212793Sdim dlerror()); 145212793Sdim return (-1); 146212793Sdim } 147212793Sdim 148218893Sdim /* 149218893Sdim * Not implementing these is not considered a failure condition, 150218893Sdim * although we might want to consider warning if obvious stuff is 151212793Sdim * not implemented, such as am_record. 152212793Sdim */ 153212793Sdim am->am_attach = dlsym(am->am_dlhandle, AUDIT_FILTER_ATTACH_STRING); 154212793Sdim am->am_reinit = dlsym(am->am_dlhandle, AUDIT_FILTER_REINIT_STRING); 155212793Sdim am->am_record = dlsym(am->am_dlhandle, AUDIT_FILTER_RECORD_STRING); 156212793Sdim am->am_rawrecord = dlsym(am->am_dlhandle, 157218893Sdim AUDIT_FILTER_RAWRECORD_STRING); 158218893Sdim am->am_detach = dlsym(am->am_dlhandle, AUDIT_FILTER_DETACH_STRING); 159218893Sdim 160212793Sdim if (am->am_attach != NULL) { 161212793Sdim if (am->am_attach(am, am->am_argc, am->am_argv) 162212793Sdim != AUDIT_FILTER_SUCCESS) { 163212793Sdim warnx("auditfilter_module_attach: %s: failed", 164212793Sdim am->am_modulename); 165212793Sdim dlclose(am->am_dlhandle); 166212793Sdim am->am_dlhandle = NULL; 167218893Sdim am->am_cookie = NULL; 168218893Sdim am->am_attach = NULL; 169218893Sdim am->am_reinit = NULL; 170212793Sdim am->am_record = NULL; 171218893Sdim am->am_rawrecord = NULL; 172212793Sdim am->am_detach = NULL; 173212793Sdim return (-1); 174212793Sdim } 175218893Sdim } 176212793Sdim 177218893Sdim return (0); 178218893Sdim} 179218893Sdim 180218893Sdim/* 181218893Sdim * When the arguments for a module are changed, we notify the module through 182218893Sdim * a call to its reinit method, if any. Return 0 on success, or -1 on 183218893Sdim * failure. 184212793Sdim */ 185212793Sdimstatic int 186212793Sdimauditfilter_module_reinit(struct auditfilter_module *am) 187212793Sdim{ 188212793Sdim 189212793Sdim if (am->am_reinit == NULL) 190218893Sdim return (0); 191218893Sdim 192218893Sdim if (am->am_reinit(am, am->am_argc, am->am_argv) != 193212793Sdim AUDIT_FILTER_SUCCESS) { 194212793Sdim warnx("auditfilter_module_reinit: %s: failed", 195212793Sdim am->am_modulename); 196212793Sdim return (-1); 197212793Sdim } 198212793Sdim 199212793Sdim return (0); 200212793Sdim} 201218893Sdim 202212793Sdim/* 203212793Sdim * Given a configuration line, generate an auditfilter_module structure that 204212793Sdim * describes it; caller will not pass comments in, so they are not looked 205212793Sdim * for. Do not attempt to instantiate it. Will destroy the contents of 206212793Sdim * 'buffer'. 207212793Sdim * 208212793Sdim * Configuration lines consist of two parts: the module name and arguments 209212793Sdim * separated by a ':', and then a ','-delimited list of arguments. 210212793Sdim * 211212793Sdim * XXXRW: Need to decide where to send the warning output -- stderr for now. 212212793Sdim */ 213212793Sdimstruct auditfilter_module * 214212793Sdimauditfilter_module_parse(const char *filename, int linenumber, char *buffer) 215212793Sdim{ 216212793Sdim char *arguments, *module, **ap; 217212793Sdim struct auditfilter_module *am; 218212793Sdim 219 am = malloc(sizeof(*am)); 220 if (am == NULL) { 221 warn("auditfilter_module_parse: %s:%d", filename, linenumber); 222 return (NULL); 223 } 224 bzero(am, sizeof(*am)); 225 226 /* 227 * First, break out the module and arguments strings. We look for 228 * one extra argument to make sure there are no more :'s in the line. 229 * That way, we prevent modules from using argument strings that, in 230 * the future, may cause problems for adding additional columns. 231 */ 232 arguments = buffer; 233 module = strsep(&arguments, ":"); 234 if (module == NULL || arguments == NULL) { 235 warnx("auditfilter_module_parse: %s:%d: parse error", 236 filename, linenumber); 237 return (NULL); 238 } 239 240 am->am_modulename = strdup(module); 241 if (am->am_modulename == NULL) { 242 warn("auditfilter_module_parse: %s:%d", filename, linenumber); 243 auditfilter_module_free(am); 244 return (NULL); 245 } 246 247 am->am_arg_buffer = strdup(buffer); 248 if (am->am_arg_buffer == NULL) { 249 warn("auditfilter_module_parse: %s:%d", filename, linenumber); 250 auditfilter_module_free(am); 251 return (NULL); 252 } 253 254 /* 255 * Now, break out the arguments string into a series of arguments. 256 * This is a bit more complicated, and requires cleanup if things go 257 * wrong. 258 */ 259 am->am_argv = malloc(sizeof(char *) * AUDITFILTERD_CONF_MAXARGS); 260 if (am->am_argv == NULL) { 261 warn("auditfilter_module_parse: %s:%d", filename, linenumber); 262 auditfilter_module_free(am); 263 return (NULL); 264 } 265 bzero(am->am_argv, sizeof(char *) * AUDITFILTERD_CONF_MAXARGS); 266 am->am_argc = 0; 267 for (ap = am->am_argv; (*ap = strsep(&arguments, " \t")) != NULL;) { 268 if (**ap != '\0') { 269 am->am_argc++; 270 if (++ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS]) 271 break; 272 } 273 } 274 if (ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS]) { 275 warnx("auditfilter_module_parse: %s:%d: too many arguments", 276 filename, linenumber); 277 auditfilter_module_free(am); 278 return (NULL); 279 } 280 281 return (am); 282} 283 284/* 285 * Read a configuration file, and populate 'list' with the configuration 286 * lines. Does not attempt to instantiate the configuration, just read it 287 * into a useful set of data structures. 288 */ 289static int 290auditfilterd_conf_read(const char *filename, FILE *fp, 291 struct auditfilter_module_list *list) 292{ 293 int error, linenumber, syntaxerror; 294 struct auditfilter_module *am; 295 char buffer[LINE_MAX]; 296 297 syntaxerror = 0; 298 linenumber = 0; 299 while (!feof(fp) && !ferror(fp)) { 300 if (fgets(buffer, LINE_MAX, fp) == NULL) 301 break; 302 linenumber++; 303 if (buffer[0] == '#' || strlen(buffer) < 1) 304 continue; 305 buffer[strlen(buffer)-1] = '\0'; 306 am = auditfilter_module_parse(filename, linenumber, buffer); 307 if (am == NULL) { 308 syntaxerror = 1; 309 break; 310 } 311 TAILQ_INSERT_HEAD(list, am, am_list); 312 } 313 314 /* 315 * File I/O error. 316 */ 317 if (ferror(fp)) { 318 error = errno; 319 auditfilter_module_list_free(list); 320 errno = error; 321 return (-1); 322 } 323 324 /* 325 * Syntax error. 326 */ 327 if (syntaxerror) { 328 auditfilter_module_list_free(list); 329 errno = EINVAL; 330 return (-1); 331 } 332 return (0); 333} 334 335/* 336 * Apply changes necessary to bring a new configuration into force. The new 337 * configuration data is passed in, and the current configuration is updated 338 * to match it. The contents of 'list' are freed or otherwise disposed of 339 * before return. 340 * 341 * The algorithms here are not very efficient, but this is an infrequent 342 * operation on very short lists. 343 */ 344static void 345auditfilterd_conf_apply(struct auditfilter_module_list *list) 346{ 347 struct auditfilter_module *am1, *am2, *am_tmp; 348 int argc_tmp, found; 349 char **argv_tmp; 350 351 /* 352 * First, remove remove and detach any entries that appear in the 353 * current configuration, but not the new configuration. 354 */ 355 TAILQ_FOREACH_SAFE(am1, &filter_list, am_list, am_tmp) { 356 found = 0; 357 TAILQ_FOREACH(am2, list, am_list) { 358 if (strcmp(am1->am_modulename, am2->am_modulename) 359 == 0) { 360 found = 1; 361 break; 362 } 363 } 364 if (found) 365 continue; 366 367 /* 368 * am1 appears in filter_list, but not the new list, detach 369 * and free the module. 370 */ 371 warnx("detaching module %s", am1->am_modulename); 372 TAILQ_REMOVE(&filter_list, am1, am_list); 373 auditfilter_module_detach(am1); 374 auditfilter_module_free(am1); 375 } 376 377 /* 378 * Next, update the configuration of any modules that appear in both 379 * lists. We do this by swapping the two argc and argv values and 380 * freeing the new one, rather than detaching the old one and 381 * attaching the new one. That way module state is preserved. 382 */ 383 TAILQ_FOREACH(am1, &filter_list, am_list) { 384 found = 0; 385 TAILQ_FOREACH(am2, list, am_list) { 386 if (strcmp(am1->am_modulename, am2->am_modulename) 387 == 0) { 388 found = 1; 389 break; 390 } 391 } 392 if (!found) 393 continue; 394 395 /* 396 * Swap the arguments. 397 */ 398 argc_tmp = am1->am_argc; 399 argv_tmp = am1->am_argv; 400 am1->am_argc = am2->am_argc; 401 am1->am_argv = am2->am_argv; 402 am2->am_argc = argc_tmp; 403 am2->am_argv = argv_tmp; 404 405 /* 406 * The reinit is a bit tricky: if reinit fails, we actually 407 * remove the old entry and detach that, as we don't allow 408 * running modules to be out of sync with the configuration 409 * file. 410 */ 411 warnx("reiniting module %s", am1->am_modulename); 412 if (auditfilter_module_reinit(am1) != 0) { 413 warnx("reinit failed for module %s, detaching", 414 am1->am_modulename); 415 TAILQ_REMOVE(&filter_list, am1, am_list); 416 auditfilter_module_detach(am1); 417 auditfilter_module_free(am1); 418 } 419 420 /* 421 * Free the entry from the new list, which will discard the 422 * old arguments. No need to detach, as it was never 423 * attached in the first place. 424 */ 425 TAILQ_REMOVE(list, am2, am_list); 426 auditfilter_module_free(am2); 427 } 428 429 /* 430 * Finally, attach any new entries that don't appear in the old 431 * configuration, and if they attach successfully, move them to the 432 * real configuration list. 433 */ 434 TAILQ_FOREACH(am1, list, am_list) { 435 found = 0; 436 TAILQ_FOREACH(am2, &filter_list, am_list) { 437 if (strcmp(am1->am_modulename, am2->am_modulename) 438 == 0) { 439 found = 1; 440 break; 441 } 442 } 443 if (found) 444 continue; 445 /* 446 * Attach the entry. If it succeeds, add to filter_list, 447 * otherwise, free. No need to detach if attach failed. 448 */ 449 warnx("attaching module %s", am1->am_modulename); 450 TAILQ_REMOVE(list, am1, am_list); 451 if (auditfilter_module_attach(am1) != 0) { 452 warnx("attaching module %s failed", 453 am1->am_modulename); 454 auditfilter_module_free(am1); 455 } else 456 TAILQ_INSERT_HEAD(&filter_list, am1, am_list); 457 } 458 459 if (TAILQ_FIRST(list) != NULL) 460 warnx("auditfilterd_conf_apply: new list not empty\n"); 461} 462 463/* 464 * Read the new configuration file into a local list. If the configuration 465 * file is parsed OK, then apply the changes. 466 */ 467int 468auditfilterd_conf(const char *filename, FILE *fp) 469{ 470 struct auditfilter_module_list list; 471 472 TAILQ_INIT(&list); 473 if (auditfilterd_conf_read(filename, fp, &list) < 0) 474 return (-1); 475 476 auditfilterd_conf_apply(&list); 477 478 return (0); 479} 480 481/* 482 * Detach and free all active filter modules for daemon shutdown. 483 */ 484void 485auditfilterd_conf_shutdown(void) 486{ 487 488 auditfilter_module_list_detach(&filter_list); 489 auditfilter_module_list_free(&filter_list); 490} 491 492/* 493 * APIs to allow modules to query and set their per-instance cookie. 494 */ 495void 496audit_filter_getcookie(void *instance, void **cookie) 497{ 498 struct auditfilter_module *am; 499 500 am = (struct auditfilter_module *)instance; 501 *cookie = am->am_cookie; 502} 503 504void 505audit_filter_setcookie(void *instance, void *cookie) 506{ 507 struct auditfilter_module *am; 508 509 am = (struct auditfilter_module *)instance; 510 am->am_cookie = cookie; 511} 512