From df5afa4fcd9725380f94ca6476248d4cc24f889a Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Sun, 29 Nov 2015 08:22:13 -0800 Subject: v2.4.4 audit sources Change-Id: I9315a7408817db51edf084fb4d27fbb492785084 Signed-off-by: Ashlee Young --- framework/src/audit/AUTHORS | 3 + framework/src/audit/COPYING | 340 + framework/src/audit/COPYING.LIB | 515 ++ framework/src/audit/ChangeLog | 396 ++ framework/src/audit/INSTALL.tmp | 11 + framework/src/audit/Makefile.am | 36 + framework/src/audit/NEWS | 0 framework/src/audit/README | 99 + framework/src/audit/THANKS | 18 + framework/src/audit/TODO | 61 + framework/src/audit/audisp/Makefile.am | 40 + framework/src/audit/audisp/audispd-builtins.c | 330 + framework/src/audit/audisp/audispd-builtins.h | 43 + framework/src/audit/audisp/audispd-config.c | 507 ++ framework/src/audit/audisp/audispd-config.h | 48 + framework/src/audit/audisp/audispd-llist.c | 157 + framework/src/audit/audisp/audispd-llist.h | 60 + framework/src/audit/audisp/audispd-pconfig.c | 516 ++ framework/src/audit/audisp/audispd-pconfig.h | 57 + framework/src/audit/audisp/audispd.c | 854 +++ framework/src/audit/audisp/plugins/Makefile.am | 32 + .../src/audit/audisp/plugins/builtins/Makefile.am | 39 + .../src/audit/audisp/plugins/builtins/af_unix.conf | 14 + .../src/audit/audisp/plugins/builtins/syslog.conf | 13 + .../src/audit/audisp/plugins/prelude/Makefile.am | 50 + .../audit/audisp/plugins/prelude/au-prelude.conf | 12 + .../src/audit/audisp/plugins/prelude/audisp-int.c | 114 + .../src/audit/audisp/plugins/prelude/audisp-int.h | 57 + .../audit/audisp/plugins/prelude/audisp-prelude.8 | 72 + .../audit/audisp/plugins/prelude/audisp-prelude.c | 2250 +++++++ .../audisp/plugins/prelude/audisp-prelude.conf | 61 + .../audisp/plugins/prelude/audisp-prelude.conf.5 | 153 + .../audit/audisp/plugins/prelude/prelude-config.c | 844 +++ .../audit/audisp/plugins/prelude/prelude-config.h | 76 + .../src/audit/audisp/plugins/remote/Makefile.am | 51 + .../src/audit/audisp/plugins/remote/au-remote.conf | 12 + .../audit/audisp/plugins/remote/audisp-remote.8 | 34 + .../audit/audisp/plugins/remote/audisp-remote.c | 1486 +++++ .../audit/audisp/plugins/remote/audisp-remote.conf | 31 + .../audisp/plugins/remote/audisp-remote.conf.5 | 211 + .../src/audit/audisp/plugins/remote/notes.txt | 31 + framework/src/audit/audisp/plugins/remote/queue.c | 574 ++ framework/src/audit/audisp/plugins/remote/queue.h | 66 + .../audit/audisp/plugins/remote/remote-config.c | 780 +++ .../audit/audisp/plugins/remote/remote-config.h | 78 + .../src/audit/audisp/plugins/remote/remote-fgets.c | 123 + .../src/audit/audisp/plugins/remote/remote-fgets.h | 33 + .../src/audit/audisp/plugins/remote/test-queue.c | 367 ++ .../audit/audisp/plugins/zos-remote/Makefile.am | 52 + .../plugins/zos-remote/audispd-zos-remote.conf | 14 + .../audisp/plugins/zos-remote/zos-remote-config.c | 443 ++ .../audisp/plugins/zos-remote/zos-remote-config.h | 48 + .../audisp/plugins/zos-remote/zos-remote-ldap.c | 608 ++ .../audisp/plugins/zos-remote/zos-remote-ldap.h | 312 + .../audisp/plugins/zos-remote/zos-remote-log.c | 109 + .../audisp/plugins/zos-remote/zos-remote-log.h | 58 + .../audisp/plugins/zos-remote/zos-remote-plugin.c | 580 ++ .../audisp/plugins/zos-remote/zos-remote-queue.c | 144 + .../audisp/plugins/zos-remote/zos-remote-queue.h | 38 + .../audisp/plugins/zos-remote/zos-remote.conf | 10 + framework/src/audit/audisp/queue.c | 226 + framework/src/audit/audisp/queue.h | 45 + framework/src/audit/audit.spec | 301 + framework/src/audit/auparse/Makefile.am | 491 ++ framework/src/audit/auparse/accesstab.h | 27 + framework/src/audit/auparse/auditd-config.c | 445 ++ framework/src/audit/auparse/auparse-defs.h | 98 + framework/src/audit/auparse/auparse-idata.h | 49 + framework/src/audit/auparse/auparse.c | 1377 ++++ framework/src/audit/auparse/auparse.h | 112 + framework/src/audit/auparse/auparse.pc.in | 11 + framework/src/audit/auparse/captab.h | 62 + framework/src/audit/auparse/clocktab.h | 36 + framework/src/audit/auparse/clone-flagtab.h | 47 + framework/src/audit/auparse/data_buf.c | 394 ++ framework/src/audit/auparse/data_buf.h | 80 + framework/src/audit/auparse/ellist.c | 428 ++ framework/src/audit/auparse/ellist.h | 66 + framework/src/audit/auparse/epoll_ctl.h | 27 + framework/src/audit/auparse/expression.c | 1111 ++++ framework/src/audit/auparse/expression.h | 133 + framework/src/audit/auparse/famtab.h | 62 + framework/src/audit/auparse/fcntl-cmdtab.h | 52 + framework/src/audit/auparse/flagtab.h | 33 + framework/src/audit/auparse/icmptypetab.h | 37 + framework/src/audit/auparse/internal.h | 86 + framework/src/audit/auparse/interpret.c | 2651 ++++++++ framework/src/audit/auparse/interpret.h | 54 + framework/src/audit/auparse/ioctlreqtab.h | 54 + framework/src/audit/auparse/ip6optnametab.h | 87 + framework/src/audit/auparse/ipccmdtab.h | 28 + framework/src/audit/auparse/ipctab.h | 37 + framework/src/audit/auparse/ipoptnametab.h | 70 + framework/src/audit/auparse/message.c | 58 + framework/src/audit/auparse/mmaptab.h | 40 + framework/src/audit/auparse/mounttab.h | 53 + framework/src/audit/auparse/nfprototab.h | 31 + framework/src/audit/auparse/nvlist.c | 137 + framework/src/audit/auparse/nvlist.h | 51 + framework/src/audit/auparse/nvpair.c | 89 + framework/src/audit/auparse/nvpair.h | 56 + framework/src/audit/auparse/open-flagtab.h | 44 + framework/src/audit/auparse/persontab.h | 45 + framework/src/audit/auparse/pktoptnametab.h | 43 + framework/src/audit/auparse/prctl-opt-tab.h | 68 + framework/src/audit/auparse/private.h | 54 + framework/src/audit/auparse/prottab.h | 28 + framework/src/audit/auparse/ptracetab.h | 55 + framework/src/audit/auparse/recvtab.h | 46 + framework/src/audit/auparse/rlimittab.h | 40 + framework/src/audit/auparse/rnode.h | 63 + framework/src/audit/auparse/schedtab.h | 31 + framework/src/audit/auparse/seccomptab.h | 30 + framework/src/audit/auparse/seektab.h | 29 + framework/src/audit/auparse/shm_modetab.h | 29 + framework/src/audit/auparse/signaltab.h | 56 + framework/src/audit/auparse/sockleveltab.h | 56 + framework/src/audit/auparse/sockoptnametab.h | 84 + framework/src/audit/auparse/socktab.h | 44 + framework/src/audit/auparse/socktypetab.h | 31 + framework/src/audit/auparse/tcpoptnametab.h | 49 + framework/src/audit/auparse/test/Makefile.am | 91 + framework/src/audit/auparse/test/auparse_test.c | 469 ++ framework/src/audit/auparse/test/auparse_test.py | 262 + framework/src/audit/auparse/test/auparse_test.ref | 803 +++ .../src/audit/auparse/test/auparse_test.ref.py | 793 +++ framework/src/audit/auparse/test/test.log | 10 + framework/src/audit/auparse/test/test2.log | 10 + framework/src/audit/auparse/tty_named_keys.h | 409 ++ framework/src/audit/auparse/typetab.h | 127 + framework/src/audit/auparse/umounttab.h | 30 + framework/src/audit/autogen.sh | 5 + framework/src/audit/bindings/Makefile.am | 25 + framework/src/audit/bindings/golang/Makefile.am | 45 + framework/src/audit/bindings/golang/audit.go | 72 + framework/src/audit/bindings/golang/test.go | 18 + framework/src/audit/bindings/python/Makefile.am | 11 + .../src/audit/bindings/python/auparse_python.c | 1776 +++++ .../src/audit/bindings/python/python2/Makefile.am | 33 + .../src/audit/bindings/python/python3/Makefile.am | 32 + framework/src/audit/bindings/python/setup.py | 0 framework/src/audit/bindings/swig/Makefile.am | 33 + .../src/audit/bindings/swig/python/Makefile.am | 40 + .../src/audit/bindings/swig/python3/Makefile.am | 41 + framework/src/audit/bindings/swig/src/Makefile.am | 26 + framework/src/audit/bindings/swig/src/auditswig.i | 46 + framework/src/audit/compile | 347 + framework/src/audit/config.guess | 1557 +++++ framework/src/audit/config.sub | 1788 +++++ framework/src/audit/configure.ac | 398 ++ framework/src/audit/contrib/avc_snap | 90 + framework/src/audit/contrib/capp.rules | 302 + framework/src/audit/contrib/lspp.rules | 343 + framework/src/audit/contrib/nispom.rules | 148 + framework/src/audit/contrib/plugin/Makefile | 7 + .../src/audit/contrib/plugin/audisp-example.c | 229 + .../src/audit/contrib/plugin/audisp-example.conf | 10 + framework/src/audit/contrib/skeleton.c | 140 + framework/src/audit/contrib/stig.rules | 193 + framework/src/audit/depcomp | 791 +++ framework/src/audit/docs/Makefile.am | 59 + framework/src/audit/docs/audispd-zos-remote.8 | 241 + framework/src/audit/docs/audispd.8 | 60 + framework/src/audit/docs/audispd.conf.5 | 50 + framework/src/audit/docs/audit.rules.7 | 171 + framework/src/audit/docs/audit_add_rule_data.3 | 49 + framework/src/audit/docs/audit_add_watch.3 | 23 + framework/src/audit/docs/audit_delete_rule_data.3 | 23 + framework/src/audit/docs/audit_detect_machine.3 | 23 + framework/src/audit/docs/audit_encode_nv_string.3 | 26 + framework/src/audit/docs/audit_get_reply.3 | 21 + framework/src/audit/docs/audit_getloginuid.3 | 25 + framework/src/audit/docs/audit_log_acct_message.3 | 44 + .../src/audit/docs/audit_log_semanage_message.3 | 53 + .../src/audit/docs/audit_log_user_avc_message.3 | 40 + .../src/audit/docs/audit_log_user_comm_message.3 | 45 + framework/src/audit/docs/audit_log_user_command.3 | 37 + framework/src/audit/docs/audit_log_user_message.3 | 42 + framework/src/audit/docs/audit_open.3 | 34 + .../src/audit/docs/audit_request_rules_list_data.3 | 25 + .../src/audit/docs/audit_request_signal_info.3 | 33 + framework/src/audit/docs/audit_request_status.3 | 44 + framework/src/audit/docs/audit_set_backlog_limit.3 | 26 + .../src/audit/docs/audit_set_backlog_wait_time.3 | 26 + framework/src/audit/docs/audit_set_enabled.3 | 27 + framework/src/audit/docs/audit_set_failure.3 | 38 + framework/src/audit/docs/audit_set_pid.3 | 24 + framework/src/audit/docs/audit_set_rate_limit.3 | 24 + framework/src/audit/docs/audit_setloginuid.3 | 25 + .../src/audit/docs/audit_update_watch_perms.3 | 23 + framework/src/audit/docs/auditctl.8 | 315 + framework/src/audit/docs/auditd.8 | 74 + framework/src/audit/docs/auditd.conf.5 | 304 + framework/src/audit/docs/augenrules.8 | 41 + framework/src/audit/docs/auparse_add_callback.3 | 69 + framework/src/audit/docs/auparse_destroy.3 | 23 + framework/src/audit/docs/auparse_feed.3 | 111 + framework/src/audit/docs/auparse_feed_has_data.3 | 29 + framework/src/audit/docs/auparse_find_field.3 | 23 + framework/src/audit/docs/auparse_find_field_next.3 | 24 + framework/src/audit/docs/auparse_first_field.3 | 22 + framework/src/audit/docs/auparse_first_record.3 | 22 + framework/src/audit/docs/auparse_flush_feed.3 | 30 + framework/src/audit/docs/auparse_get_field_int.3 | 22 + framework/src/audit/docs/auparse_get_field_name.3 | 24 + framework/src/audit/docs/auparse_get_field_str.3 | 24 + framework/src/audit/docs/auparse_get_field_type.3 | 22 + framework/src/audit/docs/auparse_get_filename.3 | 26 + framework/src/audit/docs/auparse_get_line_number.3 | 27 + framework/src/audit/docs/auparse_get_milli.3 | 25 + framework/src/audit/docs/auparse_get_node.3 | 25 + framework/src/audit/docs/auparse_get_num_fields.3 | 22 + framework/src/audit/docs/auparse_get_num_records.3 | 22 + framework/src/audit/docs/auparse_get_record_text.3 | 22 + framework/src/audit/docs/auparse_get_serial.3 | 25 + framework/src/audit/docs/auparse_get_time.3 | 26 + framework/src/audit/docs/auparse_get_timestamp.3 | 36 + framework/src/audit/docs/auparse_get_type.3 | 23 + framework/src/audit/docs/auparse_goto_record_num.3 | 21 + framework/src/audit/docs/auparse_init.3 | 37 + framework/src/audit/docs/auparse_interpret_field.3 | 24 + framework/src/audit/docs/auparse_next_event.3 | 22 + framework/src/audit/docs/auparse_next_field.3 | 22 + framework/src/audit/docs/auparse_next_record.3 | 21 + framework/src/audit/docs/auparse_node_compare.3 | 22 + framework/src/audit/docs/auparse_reset.3 | 23 + .../src/audit/docs/auparse_timestamp_compare.3 | 22 + framework/src/audit/docs/aureport.8 | 131 + framework/src/audit/docs/ausearch-expression.5 | 241 + framework/src/audit/docs/ausearch.8 | 208 + framework/src/audit/docs/ausearch_add_expression.3 | 71 + .../src/audit/docs/ausearch_add_interpreted_item.3 | 60 + framework/src/audit/docs/ausearch_add_item.3 | 60 + framework/src/audit/docs/ausearch_add_regex.3 | 31 + .../src/audit/docs/ausearch_add_timestamp_item.3 | 57 + .../audit/docs/ausearch_add_timestamp_item_ex.3 | 57 + framework/src/audit/docs/ausearch_clear.3 | 23 + framework/src/audit/docs/ausearch_next_event.3 | 24 + framework/src/audit/docs/ausearch_set_stop.3 | 37 + framework/src/audit/docs/autrace.8 | 38 + framework/src/audit/docs/get_auditfail_action.3 | 79 + framework/src/audit/docs/libaudit.conf.5 | 25 + framework/src/audit/docs/set_aumessage_mode.3 | 56 + framework/src/audit/docs/zos-remote.conf.5 | 69 + framework/src/audit/init.d/Makefile.am | 82 + framework/src/audit/init.d/audispd.conf | 12 + framework/src/audit/init.d/audit.rules | 14 + framework/src/audit/init.d/auditd.condrestart | 7 + framework/src/audit/init.d/auditd.conf | 32 + framework/src/audit/init.d/auditd.cron | 14 + framework/src/audit/init.d/auditd.init | 175 + framework/src/audit/init.d/auditd.restart | 13 + framework/src/audit/init.d/auditd.resume | 16 + framework/src/audit/init.d/auditd.rotate | 16 + framework/src/audit/init.d/auditd.service | 22 + framework/src/audit/init.d/auditd.stop | 16 + framework/src/audit/init.d/auditd.sysconfig | 24 + framework/src/audit/init.d/augenrules | 130 + framework/src/audit/init.d/libaudit.conf | 7 + framework/src/audit/install-sh | 527 ++ framework/src/audit/lib/Makefile.am | 265 + framework/src/audit/lib/aarch64_table.h | 288 + framework/src/audit/lib/actiontab.h | 25 + framework/src/audit/lib/alpha_table.h | 453 ++ framework/src/audit/lib/arm_table.h | 373 ++ framework/src/audit/lib/audit.pc.in | 10 + framework/src/audit/lib/audit_logging.c | 746 +++ framework/src/audit/lib/deprecated.c | 77 + framework/src/audit/lib/dso.h | 45 + framework/src/audit/lib/errormsg.h | 66 + framework/src/audit/lib/errtab.h | 154 + framework/src/audit/lib/fieldtab.h | 68 + framework/src/audit/lib/flagtab.h | 26 + framework/src/audit/lib/ftypetab.h | 30 + framework/src/audit/lib/gen_tables.c | 418 ++ framework/src/audit/lib/gen_tables.h | 102 + framework/src/audit/lib/i386_table.h | 379 ++ framework/src/audit/lib/ia64_table.h | 335 + framework/src/audit/lib/libaudit.c | 1606 +++++ framework/src/audit/lib/libaudit.h | 584 ++ framework/src/audit/lib/lookup_table.c | 359 + framework/src/audit/lib/machinetab.h | 47 + framework/src/audit/lib/message.c | 59 + framework/src/audit/lib/msg_typetab.h | 214 + framework/src/audit/lib/netlink.c | 299 + framework/src/audit/lib/optab.h | 31 + framework/src/audit/lib/ppc_table.h | 379 ++ framework/src/audit/lib/private.h | 175 + framework/src/audit/lib/s390_table.h | 354 + framework/src/audit/lib/s390x_table.h | 319 + framework/src/audit/lib/strsplit.c | 91 + framework/src/audit/lib/syscall-update.txt | 20 + framework/src/audit/lib/test/Makefile.am | 25 + framework/src/audit/lib/test/lookup_test.c | 430 ++ framework/src/audit/lib/x86_64_table.h | 345 + framework/src/audit/ltmain.sh | 6909 ++++++++++++++++++++ framework/src/audit/m4/ax_prog_cc_for_build.m4 | 125 + framework/src/audit/m4/cap-ng.m4 | 40 + framework/src/audit/missing | 215 + framework/src/audit/py-compile | 160 + framework/src/audit/src/Makefile.am | 57 + framework/src/audit/src/auditctl-listing.c | 577 ++ framework/src/audit/src/auditctl-listing.h | 34 + framework/src/audit/src/auditctl-llist.c | 105 + framework/src/audit/src/auditctl-llist.h | 56 + framework/src/audit/src/auditctl.c | 1472 +++++ framework/src/audit/src/auditd-config.c | 1744 +++++ framework/src/audit/src/auditd-config.h | 100 + framework/src/audit/src/auditd-dispatch.c | 213 + framework/src/audit/src/auditd-dispatch.h | 37 + framework/src/audit/src/auditd-event.c | 1407 ++++ framework/src/audit/src/auditd-event.h | 49 + framework/src/audit/src/auditd-listen.c | 1065 +++ framework/src/audit/src/auditd-listen.h | 55 + framework/src/audit/src/auditd-reconfig.c | 128 + framework/src/audit/src/auditd-sendmail.c | 116 + framework/src/audit/src/auditd.c | 917 +++ framework/src/audit/src/aureport-options.c | 722 ++ framework/src/audit/src/aureport-options.h | 55 + framework/src/audit/src/aureport-output.c | 1023 +++ framework/src/audit/src/aureport-scan.c | 974 +++ framework/src/audit/src/aureport-scan.h | 76 + framework/src/audit/src/aureport.c | 338 + framework/src/audit/src/ausearch-avc.c | 222 + framework/src/audit/src/ausearch-avc.h | 72 + framework/src/audit/src/ausearch-checkpt.c | 263 + framework/src/audit/src/ausearch-checkpt.h | 42 + framework/src/audit/src/ausearch-common.h | 73 + framework/src/audit/src/ausearch-int.c | 162 + framework/src/audit/src/ausearch-int.h | 58 + framework/src/audit/src/ausearch-llist.c | 257 + framework/src/audit/src/ausearch-llist.h | 117 + framework/src/audit/src/ausearch-lol.c | 296 + framework/src/audit/src/ausearch-lol.h | 54 + framework/src/audit/src/ausearch-lookup.c | 500 ++ framework/src/audit/src/ausearch-lookup.h | 50 + framework/src/audit/src/ausearch-match.c | 364 ++ framework/src/audit/src/ausearch-nvpair.c | 97 + framework/src/audit/src/ausearch-nvpair.h | 57 + framework/src/audit/src/ausearch-options.c | 1175 ++++ framework/src/audit/src/ausearch-options.h | 52 + framework/src/audit/src/ausearch-parse.c | 2310 +++++++ framework/src/audit/src/ausearch-parse.h | 33 + framework/src/audit/src/ausearch-report.c | 362 + framework/src/audit/src/ausearch-string.c | 178 + framework/src/audit/src/ausearch-string.h | 59 + framework/src/audit/src/ausearch-time.c | 412 ++ framework/src/audit/src/ausearch-time.h | 38 + framework/src/audit/src/ausearch.c | 594 ++ framework/src/audit/src/autrace.c | 329 + framework/src/audit/src/delete_all.c | 109 + framework/src/audit/src/libev/Makefile.am | 29 + framework/src/audit/src/libev/README | 58 + framework/src/audit/src/libev/ev.c | 4971 ++++++++++++++ framework/src/audit/src/libev/ev.h | 854 +++ framework/src/audit/src/libev/ev_epoll.c | 279 + framework/src/audit/src/libev/ev_poll.c | 148 + framework/src/audit/src/libev/ev_select.c | 314 + framework/src/audit/src/libev/ev_vars.h | 204 + framework/src/audit/src/libev/ev_wrap.h | 200 + framework/src/audit/src/libev/event.c | 425 ++ framework/src/audit/src/libev/event.h | 177 + framework/src/audit/src/libev/libev.m4 | 42 + framework/src/audit/src/mt/Makefile.am | 49 + framework/src/audit/src/test/Makefile.am | 26 + framework/src/audit/src/test/ilist_test.c | 69 + framework/src/audit/src/test/slist_test.c | 98 + framework/src/audit/tools/Makefile.am | 26 + framework/src/audit/tools/aulast/Makefile.am | 34 + framework/src/audit/tools/aulast/aulast-llist.c | 196 + framework/src/audit/tools/aulast/aulast-llist.h | 75 + framework/src/audit/tools/aulast/aulast.8 | 47 + framework/src/audit/tools/aulast/aulast.c | 610 ++ framework/src/audit/tools/aulastlog/Makefile.am | 34 + .../src/audit/tools/aulastlog/aulastlog-llist.c | 148 + .../src/audit/tools/aulastlog/aulastlog-llist.h | 65 + framework/src/audit/tools/aulastlog/aulastlog.8 | 24 + framework/src/audit/tools/aulastlog/aulastlog.c | 169 + framework/src/audit/tools/ausyscall/Makefile.am | 32 + framework/src/audit/tools/ausyscall/ausyscall.8 | 34 + framework/src/audit/tools/ausyscall/ausyscall.c | 155 + framework/src/audit/tools/auvirt/Makefile.am | 40 + framework/src/audit/tools/auvirt/auvirt-list.c | 105 + framework/src/audit/tools/auvirt/auvirt-list.h | 31 + framework/src/audit/tools/auvirt/auvirt.8 | 121 + framework/src/audit/tools/auvirt/auvirt.c | 1595 +++++ 386 files changed, 89190 insertions(+) create mode 100644 framework/src/audit/AUTHORS create mode 100644 framework/src/audit/COPYING create mode 100644 framework/src/audit/COPYING.LIB create mode 100644 framework/src/audit/ChangeLog create mode 100644 framework/src/audit/INSTALL.tmp create mode 100644 framework/src/audit/Makefile.am create mode 100644 framework/src/audit/NEWS create mode 100644 framework/src/audit/README create mode 100644 framework/src/audit/THANKS create mode 100644 framework/src/audit/TODO create mode 100644 framework/src/audit/audisp/Makefile.am create mode 100644 framework/src/audit/audisp/audispd-builtins.c create mode 100644 framework/src/audit/audisp/audispd-builtins.h create mode 100644 framework/src/audit/audisp/audispd-config.c create mode 100644 framework/src/audit/audisp/audispd-config.h create mode 100644 framework/src/audit/audisp/audispd-llist.c create mode 100644 framework/src/audit/audisp/audispd-llist.h create mode 100644 framework/src/audit/audisp/audispd-pconfig.c create mode 100644 framework/src/audit/audisp/audispd-pconfig.h create mode 100644 framework/src/audit/audisp/audispd.c create mode 100644 framework/src/audit/audisp/plugins/Makefile.am create mode 100644 framework/src/audit/audisp/plugins/builtins/Makefile.am create mode 100644 framework/src/audit/audisp/plugins/builtins/af_unix.conf create mode 100644 framework/src/audit/audisp/plugins/builtins/syslog.conf create mode 100644 framework/src/audit/audisp/plugins/prelude/Makefile.am create mode 100644 framework/src/audit/audisp/plugins/prelude/au-prelude.conf create mode 100644 framework/src/audit/audisp/plugins/prelude/audisp-int.c create mode 100644 framework/src/audit/audisp/plugins/prelude/audisp-int.h create mode 100644 framework/src/audit/audisp/plugins/prelude/audisp-prelude.8 create mode 100644 framework/src/audit/audisp/plugins/prelude/audisp-prelude.c create mode 100644 framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf create mode 100644 framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf.5 create mode 100644 framework/src/audit/audisp/plugins/prelude/prelude-config.c create mode 100644 framework/src/audit/audisp/plugins/prelude/prelude-config.h create mode 100644 framework/src/audit/audisp/plugins/remote/Makefile.am create mode 100644 framework/src/audit/audisp/plugins/remote/au-remote.conf create mode 100644 framework/src/audit/audisp/plugins/remote/audisp-remote.8 create mode 100644 framework/src/audit/audisp/plugins/remote/audisp-remote.c create mode 100644 framework/src/audit/audisp/plugins/remote/audisp-remote.conf create mode 100644 framework/src/audit/audisp/plugins/remote/audisp-remote.conf.5 create mode 100644 framework/src/audit/audisp/plugins/remote/notes.txt create mode 100644 framework/src/audit/audisp/plugins/remote/queue.c create mode 100644 framework/src/audit/audisp/plugins/remote/queue.h create mode 100644 framework/src/audit/audisp/plugins/remote/remote-config.c create mode 100644 framework/src/audit/audisp/plugins/remote/remote-config.h create mode 100644 framework/src/audit/audisp/plugins/remote/remote-fgets.c create mode 100644 framework/src/audit/audisp/plugins/remote/remote-fgets.h create mode 100644 framework/src/audit/audisp/plugins/remote/test-queue.c create mode 100644 framework/src/audit/audisp/plugins/zos-remote/Makefile.am create mode 100644 framework/src/audit/audisp/plugins/zos-remote/audispd-zos-remote.conf create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.c create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.h create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.h create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.c create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.h create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-queue.c create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote-queue.h create mode 100644 framework/src/audit/audisp/plugins/zos-remote/zos-remote.conf create mode 100644 framework/src/audit/audisp/queue.c create mode 100644 framework/src/audit/audisp/queue.h create mode 100644 framework/src/audit/audit.spec create mode 100644 framework/src/audit/auparse/Makefile.am create mode 100644 framework/src/audit/auparse/accesstab.h create mode 100644 framework/src/audit/auparse/auditd-config.c create mode 100644 framework/src/audit/auparse/auparse-defs.h create mode 100644 framework/src/audit/auparse/auparse-idata.h create mode 100644 framework/src/audit/auparse/auparse.c create mode 100644 framework/src/audit/auparse/auparse.h create mode 100644 framework/src/audit/auparse/auparse.pc.in create mode 100644 framework/src/audit/auparse/captab.h create mode 100644 framework/src/audit/auparse/clocktab.h create mode 100644 framework/src/audit/auparse/clone-flagtab.h create mode 100644 framework/src/audit/auparse/data_buf.c create mode 100644 framework/src/audit/auparse/data_buf.h create mode 100644 framework/src/audit/auparse/ellist.c create mode 100644 framework/src/audit/auparse/ellist.h create mode 100644 framework/src/audit/auparse/epoll_ctl.h create mode 100644 framework/src/audit/auparse/expression.c create mode 100644 framework/src/audit/auparse/expression.h create mode 100644 framework/src/audit/auparse/famtab.h create mode 100644 framework/src/audit/auparse/fcntl-cmdtab.h create mode 100644 framework/src/audit/auparse/flagtab.h create mode 100644 framework/src/audit/auparse/icmptypetab.h create mode 100644 framework/src/audit/auparse/internal.h create mode 100644 framework/src/audit/auparse/interpret.c create mode 100644 framework/src/audit/auparse/interpret.h create mode 100644 framework/src/audit/auparse/ioctlreqtab.h create mode 100644 framework/src/audit/auparse/ip6optnametab.h create mode 100644 framework/src/audit/auparse/ipccmdtab.h create mode 100644 framework/src/audit/auparse/ipctab.h create mode 100644 framework/src/audit/auparse/ipoptnametab.h create mode 100644 framework/src/audit/auparse/message.c create mode 100644 framework/src/audit/auparse/mmaptab.h create mode 100644 framework/src/audit/auparse/mounttab.h create mode 100644 framework/src/audit/auparse/nfprototab.h create mode 100644 framework/src/audit/auparse/nvlist.c create mode 100644 framework/src/audit/auparse/nvlist.h create mode 100644 framework/src/audit/auparse/nvpair.c create mode 100644 framework/src/audit/auparse/nvpair.h create mode 100644 framework/src/audit/auparse/open-flagtab.h create mode 100644 framework/src/audit/auparse/persontab.h create mode 100644 framework/src/audit/auparse/pktoptnametab.h create mode 100644 framework/src/audit/auparse/prctl-opt-tab.h create mode 100644 framework/src/audit/auparse/private.h create mode 100644 framework/src/audit/auparse/prottab.h create mode 100644 framework/src/audit/auparse/ptracetab.h create mode 100644 framework/src/audit/auparse/recvtab.h create mode 100644 framework/src/audit/auparse/rlimittab.h create mode 100644 framework/src/audit/auparse/rnode.h create mode 100644 framework/src/audit/auparse/schedtab.h create mode 100644 framework/src/audit/auparse/seccomptab.h create mode 100644 framework/src/audit/auparse/seektab.h create mode 100644 framework/src/audit/auparse/shm_modetab.h create mode 100644 framework/src/audit/auparse/signaltab.h create mode 100644 framework/src/audit/auparse/sockleveltab.h create mode 100644 framework/src/audit/auparse/sockoptnametab.h create mode 100644 framework/src/audit/auparse/socktab.h create mode 100644 framework/src/audit/auparse/socktypetab.h create mode 100644 framework/src/audit/auparse/tcpoptnametab.h create mode 100644 framework/src/audit/auparse/test/Makefile.am create mode 100644 framework/src/audit/auparse/test/auparse_test.c create mode 100755 framework/src/audit/auparse/test/auparse_test.py create mode 100644 framework/src/audit/auparse/test/auparse_test.ref create mode 100644 framework/src/audit/auparse/test/auparse_test.ref.py create mode 100644 framework/src/audit/auparse/test/test.log create mode 100644 framework/src/audit/auparse/test/test2.log create mode 100644 framework/src/audit/auparse/tty_named_keys.h create mode 100644 framework/src/audit/auparse/typetab.h create mode 100644 framework/src/audit/auparse/umounttab.h create mode 100755 framework/src/audit/autogen.sh create mode 100644 framework/src/audit/bindings/Makefile.am create mode 100644 framework/src/audit/bindings/golang/Makefile.am create mode 100644 framework/src/audit/bindings/golang/audit.go create mode 100644 framework/src/audit/bindings/golang/test.go create mode 100644 framework/src/audit/bindings/python/Makefile.am create mode 100644 framework/src/audit/bindings/python/auparse_python.c create mode 100644 framework/src/audit/bindings/python/python2/Makefile.am create mode 100644 framework/src/audit/bindings/python/python3/Makefile.am create mode 100644 framework/src/audit/bindings/python/setup.py create mode 100644 framework/src/audit/bindings/swig/Makefile.am create mode 100644 framework/src/audit/bindings/swig/python/Makefile.am create mode 100644 framework/src/audit/bindings/swig/python3/Makefile.am create mode 100644 framework/src/audit/bindings/swig/src/Makefile.am create mode 100644 framework/src/audit/bindings/swig/src/auditswig.i create mode 100755 framework/src/audit/compile create mode 100755 framework/src/audit/config.guess create mode 100755 framework/src/audit/config.sub create mode 100644 framework/src/audit/configure.ac create mode 100755 framework/src/audit/contrib/avc_snap create mode 100644 framework/src/audit/contrib/capp.rules create mode 100644 framework/src/audit/contrib/lspp.rules create mode 100644 framework/src/audit/contrib/nispom.rules create mode 100644 framework/src/audit/contrib/plugin/Makefile create mode 100644 framework/src/audit/contrib/plugin/audisp-example.c create mode 100644 framework/src/audit/contrib/plugin/audisp-example.conf create mode 100644 framework/src/audit/contrib/skeleton.c create mode 100644 framework/src/audit/contrib/stig.rules create mode 100755 framework/src/audit/depcomp create mode 100644 framework/src/audit/docs/Makefile.am create mode 100644 framework/src/audit/docs/audispd-zos-remote.8 create mode 100644 framework/src/audit/docs/audispd.8 create mode 100644 framework/src/audit/docs/audispd.conf.5 create mode 100644 framework/src/audit/docs/audit.rules.7 create mode 100644 framework/src/audit/docs/audit_add_rule_data.3 create mode 100644 framework/src/audit/docs/audit_add_watch.3 create mode 100644 framework/src/audit/docs/audit_delete_rule_data.3 create mode 100644 framework/src/audit/docs/audit_detect_machine.3 create mode 100644 framework/src/audit/docs/audit_encode_nv_string.3 create mode 100644 framework/src/audit/docs/audit_get_reply.3 create mode 100644 framework/src/audit/docs/audit_getloginuid.3 create mode 100644 framework/src/audit/docs/audit_log_acct_message.3 create mode 100644 framework/src/audit/docs/audit_log_semanage_message.3 create mode 100644 framework/src/audit/docs/audit_log_user_avc_message.3 create mode 100644 framework/src/audit/docs/audit_log_user_comm_message.3 create mode 100644 framework/src/audit/docs/audit_log_user_command.3 create mode 100644 framework/src/audit/docs/audit_log_user_message.3 create mode 100644 framework/src/audit/docs/audit_open.3 create mode 100644 framework/src/audit/docs/audit_request_rules_list_data.3 create mode 100644 framework/src/audit/docs/audit_request_signal_info.3 create mode 100644 framework/src/audit/docs/audit_request_status.3 create mode 100644 framework/src/audit/docs/audit_set_backlog_limit.3 create mode 100644 framework/src/audit/docs/audit_set_backlog_wait_time.3 create mode 100644 framework/src/audit/docs/audit_set_enabled.3 create mode 100644 framework/src/audit/docs/audit_set_failure.3 create mode 100644 framework/src/audit/docs/audit_set_pid.3 create mode 100644 framework/src/audit/docs/audit_set_rate_limit.3 create mode 100644 framework/src/audit/docs/audit_setloginuid.3 create mode 100644 framework/src/audit/docs/audit_update_watch_perms.3 create mode 100644 framework/src/audit/docs/auditctl.8 create mode 100644 framework/src/audit/docs/auditd.8 create mode 100644 framework/src/audit/docs/auditd.conf.5 create mode 100644 framework/src/audit/docs/augenrules.8 create mode 100644 framework/src/audit/docs/auparse_add_callback.3 create mode 100644 framework/src/audit/docs/auparse_destroy.3 create mode 100644 framework/src/audit/docs/auparse_feed.3 create mode 100644 framework/src/audit/docs/auparse_feed_has_data.3 create mode 100644 framework/src/audit/docs/auparse_find_field.3 create mode 100644 framework/src/audit/docs/auparse_find_field_next.3 create mode 100644 framework/src/audit/docs/auparse_first_field.3 create mode 100644 framework/src/audit/docs/auparse_first_record.3 create mode 100644 framework/src/audit/docs/auparse_flush_feed.3 create mode 100644 framework/src/audit/docs/auparse_get_field_int.3 create mode 100644 framework/src/audit/docs/auparse_get_field_name.3 create mode 100644 framework/src/audit/docs/auparse_get_field_str.3 create mode 100644 framework/src/audit/docs/auparse_get_field_type.3 create mode 100644 framework/src/audit/docs/auparse_get_filename.3 create mode 100644 framework/src/audit/docs/auparse_get_line_number.3 create mode 100644 framework/src/audit/docs/auparse_get_milli.3 create mode 100644 framework/src/audit/docs/auparse_get_node.3 create mode 100644 framework/src/audit/docs/auparse_get_num_fields.3 create mode 100644 framework/src/audit/docs/auparse_get_num_records.3 create mode 100644 framework/src/audit/docs/auparse_get_record_text.3 create mode 100644 framework/src/audit/docs/auparse_get_serial.3 create mode 100644 framework/src/audit/docs/auparse_get_time.3 create mode 100644 framework/src/audit/docs/auparse_get_timestamp.3 create mode 100644 framework/src/audit/docs/auparse_get_type.3 create mode 100644 framework/src/audit/docs/auparse_goto_record_num.3 create mode 100644 framework/src/audit/docs/auparse_init.3 create mode 100644 framework/src/audit/docs/auparse_interpret_field.3 create mode 100644 framework/src/audit/docs/auparse_next_event.3 create mode 100644 framework/src/audit/docs/auparse_next_field.3 create mode 100644 framework/src/audit/docs/auparse_next_record.3 create mode 100644 framework/src/audit/docs/auparse_node_compare.3 create mode 100644 framework/src/audit/docs/auparse_reset.3 create mode 100644 framework/src/audit/docs/auparse_timestamp_compare.3 create mode 100644 framework/src/audit/docs/aureport.8 create mode 100644 framework/src/audit/docs/ausearch-expression.5 create mode 100644 framework/src/audit/docs/ausearch.8 create mode 100644 framework/src/audit/docs/ausearch_add_expression.3 create mode 100644 framework/src/audit/docs/ausearch_add_interpreted_item.3 create mode 100644 framework/src/audit/docs/ausearch_add_item.3 create mode 100644 framework/src/audit/docs/ausearch_add_regex.3 create mode 100644 framework/src/audit/docs/ausearch_add_timestamp_item.3 create mode 100644 framework/src/audit/docs/ausearch_add_timestamp_item_ex.3 create mode 100644 framework/src/audit/docs/ausearch_clear.3 create mode 100644 framework/src/audit/docs/ausearch_next_event.3 create mode 100644 framework/src/audit/docs/ausearch_set_stop.3 create mode 100644 framework/src/audit/docs/autrace.8 create mode 100644 framework/src/audit/docs/get_auditfail_action.3 create mode 100644 framework/src/audit/docs/libaudit.conf.5 create mode 100644 framework/src/audit/docs/set_aumessage_mode.3 create mode 100644 framework/src/audit/docs/zos-remote.conf.5 create mode 100644 framework/src/audit/init.d/Makefile.am create mode 100644 framework/src/audit/init.d/audispd.conf create mode 100644 framework/src/audit/init.d/audit.rules create mode 100644 framework/src/audit/init.d/auditd.condrestart create mode 100644 framework/src/audit/init.d/auditd.conf create mode 100644 framework/src/audit/init.d/auditd.cron create mode 100755 framework/src/audit/init.d/auditd.init create mode 100755 framework/src/audit/init.d/auditd.restart create mode 100644 framework/src/audit/init.d/auditd.resume create mode 100644 framework/src/audit/init.d/auditd.rotate create mode 100644 framework/src/audit/init.d/auditd.service create mode 100644 framework/src/audit/init.d/auditd.stop create mode 100644 framework/src/audit/init.d/auditd.sysconfig create mode 100644 framework/src/audit/init.d/augenrules create mode 100644 framework/src/audit/init.d/libaudit.conf create mode 100755 framework/src/audit/install-sh create mode 100644 framework/src/audit/lib/Makefile.am create mode 100644 framework/src/audit/lib/aarch64_table.h create mode 100644 framework/src/audit/lib/actiontab.h create mode 100644 framework/src/audit/lib/alpha_table.h create mode 100644 framework/src/audit/lib/arm_table.h create mode 100644 framework/src/audit/lib/audit.pc.in create mode 100644 framework/src/audit/lib/audit_logging.c create mode 100644 framework/src/audit/lib/deprecated.c create mode 100644 framework/src/audit/lib/dso.h create mode 100644 framework/src/audit/lib/errormsg.h create mode 100644 framework/src/audit/lib/errtab.h create mode 100644 framework/src/audit/lib/fieldtab.h create mode 100644 framework/src/audit/lib/flagtab.h create mode 100644 framework/src/audit/lib/ftypetab.h create mode 100644 framework/src/audit/lib/gen_tables.c create mode 100644 framework/src/audit/lib/gen_tables.h create mode 100644 framework/src/audit/lib/i386_table.h create mode 100644 framework/src/audit/lib/ia64_table.h create mode 100644 framework/src/audit/lib/libaudit.c create mode 100644 framework/src/audit/lib/libaudit.h create mode 100644 framework/src/audit/lib/lookup_table.c create mode 100644 framework/src/audit/lib/machinetab.h create mode 100644 framework/src/audit/lib/message.c create mode 100644 framework/src/audit/lib/msg_typetab.h create mode 100644 framework/src/audit/lib/netlink.c create mode 100644 framework/src/audit/lib/optab.h create mode 100644 framework/src/audit/lib/ppc_table.h create mode 100644 framework/src/audit/lib/private.h create mode 100644 framework/src/audit/lib/s390_table.h create mode 100644 framework/src/audit/lib/s390x_table.h create mode 100644 framework/src/audit/lib/strsplit.c create mode 100644 framework/src/audit/lib/syscall-update.txt create mode 100644 framework/src/audit/lib/test/Makefile.am create mode 100644 framework/src/audit/lib/test/lookup_test.c create mode 100644 framework/src/audit/lib/x86_64_table.h create mode 100644 framework/src/audit/ltmain.sh create mode 100644 framework/src/audit/m4/ax_prog_cc_for_build.m4 create mode 100644 framework/src/audit/m4/cap-ng.m4 create mode 100755 framework/src/audit/missing create mode 100755 framework/src/audit/py-compile create mode 100644 framework/src/audit/src/Makefile.am create mode 100644 framework/src/audit/src/auditctl-listing.c create mode 100644 framework/src/audit/src/auditctl-listing.h create mode 100644 framework/src/audit/src/auditctl-llist.c create mode 100644 framework/src/audit/src/auditctl-llist.h create mode 100644 framework/src/audit/src/auditctl.c create mode 100644 framework/src/audit/src/auditd-config.c create mode 100644 framework/src/audit/src/auditd-config.h create mode 100644 framework/src/audit/src/auditd-dispatch.c create mode 100644 framework/src/audit/src/auditd-dispatch.h create mode 100644 framework/src/audit/src/auditd-event.c create mode 100644 framework/src/audit/src/auditd-event.h create mode 100644 framework/src/audit/src/auditd-listen.c create mode 100644 framework/src/audit/src/auditd-listen.h create mode 100644 framework/src/audit/src/auditd-reconfig.c create mode 100644 framework/src/audit/src/auditd-sendmail.c create mode 100644 framework/src/audit/src/auditd.c create mode 100644 framework/src/audit/src/aureport-options.c create mode 100644 framework/src/audit/src/aureport-options.h create mode 100644 framework/src/audit/src/aureport-output.c create mode 100644 framework/src/audit/src/aureport-scan.c create mode 100644 framework/src/audit/src/aureport-scan.h create mode 100644 framework/src/audit/src/aureport.c create mode 100644 framework/src/audit/src/ausearch-avc.c create mode 100644 framework/src/audit/src/ausearch-avc.h create mode 100644 framework/src/audit/src/ausearch-checkpt.c create mode 100644 framework/src/audit/src/ausearch-checkpt.h create mode 100644 framework/src/audit/src/ausearch-common.h create mode 100644 framework/src/audit/src/ausearch-int.c create mode 100644 framework/src/audit/src/ausearch-int.h create mode 100644 framework/src/audit/src/ausearch-llist.c create mode 100644 framework/src/audit/src/ausearch-llist.h create mode 100644 framework/src/audit/src/ausearch-lol.c create mode 100644 framework/src/audit/src/ausearch-lol.h create mode 100644 framework/src/audit/src/ausearch-lookup.c create mode 100644 framework/src/audit/src/ausearch-lookup.h create mode 100644 framework/src/audit/src/ausearch-match.c create mode 100644 framework/src/audit/src/ausearch-nvpair.c create mode 100644 framework/src/audit/src/ausearch-nvpair.h create mode 100644 framework/src/audit/src/ausearch-options.c create mode 100644 framework/src/audit/src/ausearch-options.h create mode 100644 framework/src/audit/src/ausearch-parse.c create mode 100644 framework/src/audit/src/ausearch-parse.h create mode 100644 framework/src/audit/src/ausearch-report.c create mode 100644 framework/src/audit/src/ausearch-string.c create mode 100644 framework/src/audit/src/ausearch-string.h create mode 100644 framework/src/audit/src/ausearch-time.c create mode 100644 framework/src/audit/src/ausearch-time.h create mode 100644 framework/src/audit/src/ausearch.c create mode 100644 framework/src/audit/src/autrace.c create mode 100644 framework/src/audit/src/delete_all.c create mode 100644 framework/src/audit/src/libev/Makefile.am create mode 100644 framework/src/audit/src/libev/README create mode 100644 framework/src/audit/src/libev/ev.c create mode 100644 framework/src/audit/src/libev/ev.h create mode 100644 framework/src/audit/src/libev/ev_epoll.c create mode 100644 framework/src/audit/src/libev/ev_poll.c create mode 100644 framework/src/audit/src/libev/ev_select.c create mode 100644 framework/src/audit/src/libev/ev_vars.h create mode 100644 framework/src/audit/src/libev/ev_wrap.h create mode 100644 framework/src/audit/src/libev/event.c create mode 100644 framework/src/audit/src/libev/event.h create mode 100644 framework/src/audit/src/libev/libev.m4 create mode 100644 framework/src/audit/src/mt/Makefile.am create mode 100644 framework/src/audit/src/test/Makefile.am create mode 100644 framework/src/audit/src/test/ilist_test.c create mode 100644 framework/src/audit/src/test/slist_test.c create mode 100644 framework/src/audit/tools/Makefile.am create mode 100644 framework/src/audit/tools/aulast/Makefile.am create mode 100644 framework/src/audit/tools/aulast/aulast-llist.c create mode 100644 framework/src/audit/tools/aulast/aulast-llist.h create mode 100644 framework/src/audit/tools/aulast/aulast.8 create mode 100644 framework/src/audit/tools/aulast/aulast.c create mode 100644 framework/src/audit/tools/aulastlog/Makefile.am create mode 100644 framework/src/audit/tools/aulastlog/aulastlog-llist.c create mode 100644 framework/src/audit/tools/aulastlog/aulastlog-llist.h create mode 100644 framework/src/audit/tools/aulastlog/aulastlog.8 create mode 100644 framework/src/audit/tools/aulastlog/aulastlog.c create mode 100644 framework/src/audit/tools/ausyscall/Makefile.am create mode 100644 framework/src/audit/tools/ausyscall/ausyscall.8 create mode 100644 framework/src/audit/tools/ausyscall/ausyscall.c create mode 100644 framework/src/audit/tools/auvirt/Makefile.am create mode 100644 framework/src/audit/tools/auvirt/auvirt-list.c create mode 100644 framework/src/audit/tools/auvirt/auvirt-list.h create mode 100644 framework/src/audit/tools/auvirt/auvirt.8 create mode 100644 framework/src/audit/tools/auvirt/auvirt.c (limited to 'framework') diff --git a/framework/src/audit/AUTHORS b/framework/src/audit/AUTHORS new file mode 100644 index 00000000..5ed9e76a --- /dev/null +++ b/framework/src/audit/AUTHORS @@ -0,0 +1,3 @@ +This program was started by Rik Faith. +It is now being maintained by Steve Grubb + diff --git a/framework/src/audit/COPYING b/framework/src/audit/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/framework/src/audit/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/framework/src/audit/COPYING.LIB b/framework/src/audit/COPYING.LIB new file mode 100644 index 00000000..ba2be481 --- /dev/null +++ b/framework/src/audit/COPYING.LIB @@ -0,0 +1,515 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper +mail. + +You should also get your employer (if you work as a programmer) or +your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James +Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/framework/src/audit/ChangeLog b/framework/src/audit/ChangeLog new file mode 100644 index 00000000..f6f05b48 --- /dev/null +++ b/framework/src/audit/ChangeLog @@ -0,0 +1,396 @@ +2.4.4 +- Fix linked list correctness in ausearch/report +- Add more cross compile fixups (Clayton Shotwell) +- Update auparse python bindings +- Update libev to 4.20 +- Fix CVE-2015-5186 Audit: log terminal emulator escape sequences handling + +2.4.3 +- Add python3 support for libaudit +- Cleanup automake warnings +- Add AuParser_search_add_timestamp_item_ex to python bindings +- Add AuParser_get_type_name to python bindings +- Correct processing of obj_gid in auditctl (Aleksander Zdyb) +- Make plugin config file parsing more robust for long lines (#1235457) +- Make auditctl status print lost field as unsigned number +- Add interpretation mode for auditctl -s +- Add python3 support to auparse library +- Make --enable-zos-remote a build time configuration option (Clayton Shotwell) +- Updates for cross compiling (Clayton Shotwell) +- Add MAC_CHECK audit event type +- Add libauparse pkgconfig file (Aleksander Zdyb) + +2.4.2 +- Ausearch should parse exe field in SECCOMP events +- Improve output for short mode interpretations in auparse +- Add CRYPTO_IKE_SA and CRYPTO_IPSEC_SA events +- If auditctl is reading rules from a file, send messages to syslog (#1144252) +- Correct lookup of ppc64le when determining machine type +- Increase time buffer for wide character numbers in ausearch/report (#1200314) +- In aureport, add USER_TTY events to tty report +- In audispd, limit reporting of queue full messages (#1203810) +- In auditctl, don't segfault when invalid options passed (#1206516) +- In autrace, remove some older unimplemented syscalls for aarch64 (#1185892) +- In auditctl, correct lookup of aarch64 in arch field (#1186313) +- Update lookup tables for 4.1 kernel + +2.4.1 +- Make python3 support easier +- Add support for ppc64le (Tony Jones) +- Add some translations for a1 of ioctl system calls +- Add command & virtualization reports to aureport +- Update aureport config report for new events +- Add account modification summary report to aureport +- Add GRP_MGMT and GRP_CHAUTHTOK event types +- Correct aureport account change reports +- Add integrity event report to aureport +- Add config change summary report to aureport +- Adjust some syslogging level settings in audispd +- Improve parsing performance in everything +- When ausearch outputs a line, use the previously parsed values (Burn Alting) +- Improve searching and interpreting groups in events +- Fully interpret the proctitle field in auparse +- Correct libaudit and auditctl support for kernel features +- Add support for backlog_time_wait setting via auditctl +- Update syscall tables for the 3.18 kernel +- Ignore DNS failure for email validation in auditd (#1138674) +- Allow rotate as action for space_left and disk_full in auditd.conf +- Correct login summary report of aureport +- Auditctl syscalls can be comma separated list now +- Update rules for new subsystems and capabilities + +2.4 +- Optionally parse loginuids, (e)uids, & (e)gids in ausearch/report +- In auvirt, anomaly events don't have uuid (#1111448) +- Fix category handling in various records (#1120286) +- Fix ausearch handling of session id on 32 bit systems +- Set systemd startup to wait until systemd-tmpfiles-setup.service (#1097314) +- Interpret a0 of socketcall and ipccall syscalls +- Add pkgconfig file for libaudit +- Add go language bindings for limited use of libaudit +- Fix ausearch handling of exit code on 32 bit systems +- Fix bug in aureport string linked list handling +- Document week-ago time setting in ausearch/report man page +- Update tables for 3.16 kernel +- In aulast, on bad logins only record user_login proof and use it +- Add libaudit API for kernel features +- If audit=0 on kernel cmnd line, skip systemd activation (Cristian Rodríguez) +- Add checkpoint --start option to ausearch (Burn Alting) +- Fix arch matching in ausearch +- Add --loginuid-immutable option to auditctl +- Fix memory leak in auditd when log_format is set to NOLOG +- Update auditctl to display features in the status command +- Add ausearch_add_timestamp_item_ex() to auparse + +2.3.7 +- Limit number of options in a rule in libaudit +- Auditctl cannot load rule with lots of syscalls (#1089713) +- In ausearch, fix checkpointing when inode is reused by new log (Burn Alting) +- Add PROCTITLE and FEATURE_CHANGE event types + +2.3.6 +- Add an option to auditctl to interpret a0 - a3 of syscall rules when listing +- Improve ARM and AARCH64 support (AKASHI Takahiro) +- Add ausearch --checkpoint feature (Burn Alting) +- Add --arch option to ausearch +- Improve too long config line in audispd, auditd, and auparse (#1071580) +- Fix aulast to accept the new AUDIT_LOGIN record format +- Remove clear_config symbol in auparse + +2.3.5 +- In CRYPTO_KEY_USER events, do not interpret the 'fp' field +- Change formatting of rules listing in auditctl to look like audit.rules +- Change auditctl to do all netlink comm and then print rules +- Add a debug option to ausearch to find skipped events +- Parse subject, auid, and ses in LOGIN events (3.14 kernel changed format) +- In auditd, when shifting logs, ignore the num_logs setting (#950158) +- Allow passing a directory as the input file for ausearch/report (LC Bruzenak) +- Interpret syscall fields in SECCOMP events +- Increase a couple buffers to handle longer input + +2.3.4 +- Parse path in CONFIG_CHANGE events +- In audisp-remote, fix retry logic for temporary network failures +- In auparse, add get_type_name function +- Add --no-config command option to aureport +- Fix interpretting MCS seliunx contexts in ausearch (#970675) +- In auparse, classify selinux contexts as MAC_LABEL field type +- In ausearch/report parse vm-ctx and img-ctx as selinux labels +- Update translation tables for the 3.14 kernel + +2.3.3 +- Documentation updates +- Add AUDIT_USER_MAC_CONFIG_CHANGE event for MAC policy changes +- Update interpreting scheduler policy names +- Update automake files to automake-1.13.4 +- Remove CAP_COMPROMISE_KERNEL interpretation +- Parse name field in AVC's (#1049916) +- Add missing typedef for auparse_type_t enumeration (#1053424) +- Fix parsing encoded filenames in records +- Parse SECCOMP events + +2.3.2 +- Put RefuseManualStop in the right systemd section (#969345) +- Add legacy restart scripts for systemd support +- Add more syscall argument interpretations +- Add 'unset' keyword for uid & gid values in auditctl +- In ausearch, parse obj in IPC records +- In ausearch, parse subj in DAEMON_ROTATE records +- Fix interpretation of MQ_OPEN and MQ_NOTIFY events +- In auditd, restart dispatcher on SIGHUP if it had previously exited +- In audispd, exit when no active plugins are detected on reconfigure +- In audispd, clear signal mask set by libev so that SIGHUP works again +- In audispd, track binary plugins and restart if binary was updated +- In audispd, make sure we send signals to the correct process +- In auditd, clear signal mask when spawning any child process +- In audispd, make builtin plugins respond to SIGHUP +- In auparse, interpret mode flags of open syscall if O_CREAT is passed +- In audisp-remote, don't make address lookup always a permanent failure +- In audisp-remote, remove EOE events more efficiently +- In auditd, log the reason when email account is not valid +- In audisp-remote, change default remote_ending action to reconnect +- Add support for Aarch64 processors + +2.3.1 +- Rearrange auditd setting enabled and pid to avoid a race (#910568) +- Interpret the ocomm field from OBJ_PID records +- Fix missing 'then' statement in sysvinit script +- Switch ausearch to use libauparse for interpretting fields +- In libauparse, interpret prctl arg0, sched_setscheduler arg1 +- In auparse, check source_list isn't NULL when opening next file (Liequan Che) +- In libauparse, interpret send* flags argument +- In libauparse, interpret level and name options for set/getsockopt +- In ausearch/report, don't flush events until last file (Burn Alting) +- Don't use systemctl to stop the audit daemon + +2.3 +- The clone(2) man page is really clone(3), fix interpretation of clone syscall +- Add systemd support for reload (#901533) +- Allow -F msgtype on the user filter +- Add legacy support for resuming logging under systemd (#830780) +- Add legacy support for rotating logs under systemd (#916611) +- In auditd, collect SIGUSR2 info for DAEMON_RESUME events +- Updated man pages +- Update libev to 4.15 +- Update syscall tables for 3.9 kernel +- Interpret MQ_OPEN events +- Add augenrules support (Burn Alting) +- Consume less stack sending audit events + +2.2.3 +- Code cleanups +- In spec file, don't own lib64/audit +- Update man pages +- Aureport no longer reads auditd.conf when stdin is used +- Don't let systemd kill auditd if auditctl errors out +- Update syscall table for 3.7 and 3.8 kernels +- Add interpretation for setns and unshare syscalls +- Code cleanup (Tyler Hicks) +- Documentation cleanups (Laurent Bigonville) +- Add dirfd interpretation to the *at functions +- Add termination signal to clone flags interpretation +- Update stig.rules +- In auditctl, when listing rules don't print numeric value of dir fields +- Add support for rng resource type in auvirt +- Fix aulast bad login output (#922508) +- In ausearch, allow negative numbers for session and auid searches +- In audisp-remote, if disk_full_action is stop then stop sending (#908977) + +2.2.2 +- In auditd, tcp_max_per_addr was allowing 1 more connection than specified +- In ausearch, fix matching of object records +- Auditctl was returning -1 when listing rules filtered on a key field +- Add interpretations for CAP_BLOCK_SUSPEND and CAP_COMPROMISE_KERNEL +- Add armv5tejl, armv5tel, armv6l and armv7l machine types (Nathaniel Husted) +- Updates for the 3.6 kernel +- Add auparse_feed_has_data function to libauparse +- Update audisp-prelude to use auparse_feed_has_data +- Add support to conditionally build auditd network listener (Tyler Hicks) +- In auditd, reset a flag after receiving USR1 signal info when rotating logs +- Add optional systemd init script support +- Add support for SECCOMP event type +- Don't interpret aN_len field in EXECVE records (#869555) +- In audisp-remote, do better job of draining queue +- Fix capability parsing in ausearch/auparse +- Interpret BPRM_FCAPS capability fields +- Add ANOM_LINK event type + +2.2.1 +- Add more interpretations in auparse for syscall parameters +- Add some interpretations to ausearch for syscall parameters +- In ausearch/report and auparse, allocate extra space for node names +- Update syscall tables for the 3.3.0 kernel +- Update libev to 4.0.4 +- Reduce the size of some applications +- In auditctl, check usage against euid rather than uid + +2.2 +- Correct all rules for clock_settime +- Fix possible segfault in auparse library +- Handle malformed socket addresses better +- Improve performance in audit_log_user_message() +- Improve performance in writing to the log file in auditd +- Syscall update for accept4 and recvmmsg +- Update autrace resource usage mode syscall list +- Improved sample rules for recent syscalls +- Add some debug info to audisp-remote startup and shutdown +- Make compiling with Python optional +- In auditd, if disk_error_action is ignore, don't syslog anything +- Fix some memory leaks +- If audispd is stopping, don't restart children +- Add support in auditctl for shell escaped filenames (Alexander) +- Add search support for virt events (Marcelo Cerri) +- Update interpretation tables +- Sync auparse's auditd config parser with auditd's parser +- In ausearch, also use cwd fields in file name searchs +- In ausearch, parse cwd in USER_CMD events +- In ausearch, correct parsing of uid in user space events +- In ausearch, update parsing of integrity events +- Apply some text cleanups from Debian (Russell Coker) +- In auditd, relax some permission checks for external apps +- Add ROLE_MODIFY event type +- In auditctl, new -c option to continue through bad rules but with failed exit +- Add auvirt program to do special reporting on virt events (Marcelo Cerri) +- Add interfield comparison support to auditctl (Peter Moody) +- Update auparse type intepretation for apparmor (Marcelo Cerri) +- Increase tcp_max_per_addr maximum to 1024. + +2.1.3 +- Fix parsing of EXECVE records to not escape argc field +- If auditd's disk is full, send the right reason to client (#715315) +- Add CAP_WAKE_ALARM to interpretations +- Some updates to audisp-remote's remote-fgets function (Mirek Trmac) +- Add detection of TTY events to audisp-prelude (Matteo Sessa) +- Updated syscall tables for the 3.0 kernel +- Update linker flags for better relro support +- Make default size of logs bigger (#727310) +- Extract obj from NETFILTER_PKT events +- Disable 2 kerberos config options in audisp-remote.conf + +2.1.2 +- In ausearch/report, fix a segfault caused by MAC_POLICY_LOAD records +- In ausearch/report, add and update parsers +- In auditd, cleanup DAEMON_ACCEPT and DAEMON_CLOSE addr fields +- In ausearch/report, parse addr field of DAEMON_ACCEPT & DAEMON_CLOSE records +- In auditd, move startup success to after events are registered +- If auditd shutsdown due to failed tcp init, write a DAEMON_ABORT event +- Update auditd to avoid the oom killer in new kernels (Andreas Jaeger) +- Parse and interpret NETFILTER_PKT events correctly +- Return error if auditctl -l fails (#709345) +- In audisp-remote, replace glibc's fgets with custom implementation + +2.1.1 +- When ausearch is interpretting, output "as is" if no = is found +- Correct socket setup in remote logging +- Adjusted a couple default settings for remote logging and init script +- Audispd was not marking restarted plugins as active +- Audisp-remote should keep a capability if local_port < 1024 +- When audispd restarts plugin, send event in its preferred format +- In audisp-remote, make all I/O asynchronous +- In audisp-remote, add sigusr1 handler to dump internal state +- Fix autrace to use correct syscalls on s390 and s390x systems +- Add shutdown syscall to remote logging teardowns +- Correct autrace rule for 32 bits systems + +2.1 +- Update auditctl man page for new field on user filter +- Fix crash in aulast when auid is foreign to the system +- Code cleanups +- Add store and forward model to audispd-remote (Mirek Trmac) +- Free memory on failed startups in audisp-prelude +- Fix memory leak in aureport +- Fix parsing state problem in libauparse +- Improve the robustness of libaudit field encoding functions +- Update capability tables +- In auditd, make failure action config checking consistent +- In auditd, check that NULL is not being passed to safe_exec +- In audisp-remote, overflow_action wasn't suspending if that action was chosen +- Update interpretations for virt events +- Improve remote logging warning and error messages +- Add interpretations for netfilter events + +2.0.6 +- ausearch/report performance improvements +- Synchronize all sample syscall rules to use action,list +- If program name provided to audit_log_acct_message, escape it +- Fix man page for the audit_encode_nv_string function (#647131) +- If value is NULL, don't segfault (#647128) +- Fix simple event parsing to not assume session id can't be last (Peng Haitao) +- Add support for new mmap audit event type +- Add ability for audispd syslog plugin to choose facility local0-7 (#593340) +- Fix autrace to use correct syscalls on i386 systems (Peng Haitao) +- On startup and reconfig, check for excess logs and unlink them +- Add a couple missing parser debug messages +- Fix error output resolving numeric address and update man page +- Add netfilter event types +- Fix spelling error in audit.rules man page (#667845) +- Improve warning in auditctl regarding immutable mode (#654883) +- Update syscall tables for the 2.6.37 kernel +- In ausearch, allow searching for auid -1 +- Add queue overflow_action to audisp-remote to control queue overflows +- Update sample rules for new syscalls and packages + +2.0.5 +- Make auparse handle empty AUSOURCE_FILE_ARRAY correctly (Miloslav Trmač) +- On i386, audit rules do not work on inode's with a large number (#554553) +- Fix displaying of inode values to be unsigned integers when listing rules +- Correct Makefile install of audispd (Jason Tang) +- Syscall table updates for 2.6.34 kernel +- Add definitions for service start and stop +- Fix handling of ignore errors in auditctl +- Fix gssapi support to build with new linker options +- Add virtualization event types +- Update aureport program help and man pages to show all options + +2.0.4 +- Make alpha processor support optional +- Add support for the arm eabi processor +- add a compatible regexp processing capability to auparse (Miloslav Trmač) +- Fix regression in parsing user space originating records in aureport +- Add tcp_max_per_addr option in auditd.conf to limit concurrent connections +- Rearrange shutdown of auditd to allow DAEMON_END event more time + +2.0.3 +- In auditd, tell libev to stop processing a connection when idle timeout +- In auditd, tell libev to stop processing a connection when shutting down +- Interpret CAPSET records in ausearch/auparse + +2.0.2 +- If audisp-remote plugin has a queue at exit, use non-zero exit code +- Fix autrace to use the exit filter +- In audisp-remote, add a sigchld handler +- In auditd, check for duplicate remote connections before accepting +- Remove trailing ':' if any are at the end of acct fields in ausearch +- Update remote logging code to do better sanity check of data +- Fix audisp-prelude to prefer files if multiple path records are encountered +- Add libaudit.conf man page +- In auditd, disconnect idle clients + +2.0.1 +- Aulast now reads daemon_start events for the kernel version of reboot +- Clarify the man pages for ausearch/report regarding locale and date formats +- Fix getloginuid for python bindings +- Disable the audispd af_unix plugin by default +- Add a couple new init script actions for LSB 3.2 +- In audisp-remote plugin, timeout network reads (#514090) +- Make some error logging in audisp-remote plugin more prominent +- Add audit.rules man page +- Interpret the session field in audit events + +2.0 +- Remove system-config-audit +- Get rid of () from userspace originating events +- Removed old syscall rules API - not needed since 2.6.16 +- Remove all use of the old rule structs from API +- Fix uninitialized variable in auditd log rotation +- Add libcap-ng support for audispd plugins +- Removed ancient defines that are part of kernel 2.6.29 headers +- Bump soname number for libaudit +- In auditctl, deprecate the entry filter and move rules to exit filter +- Parse integrity audit records in ausearch/report (Mimi Zohar) +- Updated syscall table for 2.6.31 kernel +- Remove support for the legacy negate syscall rule operator +- In auditd reset syslog warnings if disk space becomes available + + + diff --git a/framework/src/audit/INSTALL.tmp b/framework/src/audit/INSTALL.tmp new file mode 100644 index 00000000..d1e4e5af --- /dev/null +++ b/framework/src/audit/INSTALL.tmp @@ -0,0 +1,11 @@ +To build the package, try this: rpmbuild --rebuild audit-1.7.5-1.src.rpm +substituting the proper version. + +If you insist on doing it the hard way: +./configure --sbindir=/sbin --with-python=yes --with-libwrap --enable-gssapi-krb5=yes --with-libcap-ng=yes +make +make install + +If you want to do this from a svn copy, precede the above with: +./autogen.sh + diff --git a/framework/src/audit/Makefile.am b/framework/src/audit/Makefile.am new file mode 100644 index 00000000..df6915ec --- /dev/null +++ b/framework/src/audit/Makefile.am @@ -0,0 +1,36 @@ +# Makefile.am -- +# Copyright 2004-08,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# Rickard E. (Rik) Faith +# + +SUBDIRS = lib auparse src/mt src/libev src audisp tools bindings init.d \ + docs +EXTRA_DIST = ChangeLog AUTHORS NEWS README INSTALL audit.spec \ + COPYING COPYING.LIB \ + contrib/capp.rules contrib/nispom.rules contrib/lspp.rules \ + contrib/stig.rules contrib/skeleton.c contrib/avc_snap \ + contrib/plugin/Makefile contrib/plugin/audisp-example.c \ + contrib/plugin/audisp-example.conf +CONFIG_CLEAN_FILES = debug*.list config/* + +clean-generic: + rm -rf autom4te*.cache + rm -f *.rej *.orig *.lang diff --git a/framework/src/audit/NEWS b/framework/src/audit/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/framework/src/audit/README b/framework/src/audit/README new file mode 100644 index 00000000..b0804721 --- /dev/null +++ b/framework/src/audit/README @@ -0,0 +1,99 @@ +This is some background information about the Linux Auditing Framework. + +LICENSE +======= +The audit daemon is released as GPL'd code. The audit daemon's libraries +libaudit.* and libauparse.* are released under LGPL so that it may be +linked with 3rd party software. + +BUILDING +======== +See the README-install File. + +USAGE +===== +See the man pages for audit, auditctl, audit.rules, ausearch, and aureport. + +DISCUSSION +========== +Original lkml thread(s): + http://marc.theaimsgroup.com/?t=107815888100001&r=1&w=2 + http://marc.theaimsgroup.com/?t=107901570800002&r=1&w=2 + +There is a linux audit mail list where any question whether kernel design, +setup and configuration, or usage can be discussed: +http://www.redhat.com/mailman/listinfo/linux-audit + + +DESIGN INFO (Very old) +===================== +The main goals were to provide system call auditing with 1) as low +overhead as possible, and 2) without duplicating functionality that is +already provided by SELinux (and/or other security infrastructures). +This framework will work "stand-alone", but is not designed to provide, +e.g., CAPP functionality without another security component in place. + +There are two main parts, one that is always on (generic logging in +audit.c) and one that you can disable at boot- or run-time +(per-system-call auditing in auditsc.c). The patch includes changes to +security/selinux/avc.c as an example of how system-call auditing can be +integrated with other code that identifies auditable events. + +Logging: + 1) Uses a netlink socket for communication with user-space. All + messages are logged via the netlink socket if a user-space daemon + is listening. If not, the messages are logged via printk to the + syslog daemon (by default). + 2) Messages can be dropped (optionally) based on message rate or + memory use (this isn't fully integrated into the selinux/avc.c + part of the patch: the avc.c code that currently does this can be + eliminated). + 3) When some part of the kernel generates part of an audit record, + the partial record is sent immediately to user-space, AND the + system call "auditable" flag is automatically set for that call + -- thereby producing extra information at syscall exit (if + syscall auditing is enabled). + +System-call auditing: + 1) At task-creation time, an audit context is allocated and linked + off the task structure. + 2) At syscall entry time, if the audit context exists, information + is filled in (syscall number, timestamp; but not arguments). + 3) During the system call, calls to getname() and path_lookup() are + intercepted. These routines are called when the kernel is + actually looking up information that will be used to make the + decision about whether the syscall will succeed or fail. An + effort has been made to avoid copying the information that + getname generates, since getname is already making a + kernel-private copy of the information. [Note that storing + copies of all syscall arguments requires complexity and overhead + that arguably isn't needed. With this patch, for example, if + chroot("foo") fails because you are not root, "foo" will not + appear in the audit record because the kernel determined the + syscall cannot proceed before it ever needed to look up "foo". + This approach avoids storing user-supplied information that could + be misleading or unreliable (e.g., due to a cooperative + shared-memory attack) in favor of reporting information actually + used by the kernel.] + 4) At syscall exit time, if the "auditable" flag has been set (e.g., + because SELinux generated an avc record; or some other part of + the kernel detected an auditable event), the syscall-part of the + audit record is generated, including file names and inode numbers + (if available). Some of this information is currently + complementary to the information that selinux/avc.c generates + (e.g., file names and some inode numbers), but some is less + complete (e.g., getname doesn't return a fully-qualified path, + and this patch does not add the overhead of determining one). + [Note that the complete audit record comes to userspace in + pieces, which eliminates the need to store messages for + arbitrarily long periods inside the kernel.] + 5) At task-exit time, the audit context is destroyed. + + At steps 1, 2, and 4, simple filtering can be done (e.g., a database + role uid might have syscall auditing disabled for performance + reasons). The filtering is simple and could be made more complex. + However, I tried to implement as much filtering as possible without + adding significant overhead (e.g., d_path()). In general, the audit + framework should rely on some other kernel component (e.g., SELinux) + to make the majority of the decisions about what is and is not + auditable. diff --git a/framework/src/audit/THANKS b/framework/src/audit/THANKS new file mode 100644 index 00000000..9d7dbd58 --- /dev/null +++ b/framework/src/audit/THANKS @@ -0,0 +1,18 @@ +This file is to mention significant contributions +to this project. + +* Kris Wilson of IBM for all the testing and bug reports +* Tim Chavez of IBM for the auditctl filesystem watch code +* Debbie Velarde of IBM for the command line parsing code of ausearch and lspp rules sample configuration +* Amy Griffis of HP for the capp.rules sample configuration +* Dustin Kirkland of IBM for the new rule operator & exclude filter patch +* Sergey Tikhonov for the Alpha Processor support patch +* Darrel Goeddel of TCS for new audit rule format patch +* Lisa Smith of HP for the audit failure query function +* Dan Walsh of Red Hat for Python bindings +* John Dennis of Red Hat for rewriting the python bindings and auparse updates +* Miloslav Trmac of Red Hat for numerous patches including store forward remote logging +* DJ Delorie of Red Hat for the remote logging code +* Marcelo Cerri of IBM for the auvirt program +* Peter Moody of Google for the interfield comparator code +* Burn Alting for the augenrules code diff --git a/framework/src/audit/TODO b/framework/src/audit/TODO new file mode 100644 index 00000000..e568929a --- /dev/null +++ b/framework/src/audit/TODO @@ -0,0 +1,61 @@ +Things that need to be done: +=========================== +2.5 +* Add audit by process name support +* Add support for enriched data + +2.5.1 +* Fix auparse to handle out of order messages +* Add metadata in auparse for subj,obj,action,results +* Performance improvements for auparse +* auditctl should ignore invalid arches for rules +* If auparse input is a pipe timeout events by wall clock + +2.6 +* Add cross-compile support +* Add gzip format for logs +* Add keywords for time: month-ago +* Add rule verify to detect mismatch between in-kernel and on-disk rules +* Fix SIGHUP for auditd network settings +* Fix auvirt to report AVC's and --proof for --all-events + +2.6.1 +* Fix ausearch/report to handle aggregated events +* When searching, build log time list & only read the ones that are in range +* Change ausearch-string to be AVL based +* Add libaudit.m4 to make audit easier to include +* Look at adding the direction read/write to file report (threat modelling) +* Changes in uid/gid, failed changes in credentials in aureport +* aureport get specific reports working +* Remove evil getopt cruft in auditctl +* Group message types in ausearch help. + +2.7 +* Look at pulling audispd into auditd +* Consider adding node/machine name to records going to rt interface in daemon as protocol version 2. +* Fix retry logic in distribute event, buffer is freed by the logger thread +* interpret contexts +* Allow -F path!=/var/my/app +* Add ignore action for rules +* Look at openat and why passed dir is not given +* Add SYSLOG data source for auparse. This allows leading text before audit messages, missing type, any line with no = gets thrown away. iow, must have time and 1 field to be valid. +* Update auditctl so that if syscall is not found, it checks for socket call and suggests using it instead. Same for IPCcall. +* Fix aureport accounting for avc in permissive mode +* rework ausearch to use auparse +* rework aureport to use auparse + +2.8 +* Consolidate parsing code between libaudit and auditd-conf.c +* Look at variadic avc logging patch +* If relative file in cwd, need to build also (realpath). watch out for (null) and socket +* Change ausearch to output name="" unless its a real null. (mount) ausearch-report.c, 523. FIXME +* add more libaudit man pages +* ausearch --op search +* Fix aureport-scan to properly decide if CONFIG_CHANGE is add or del, need to optionally look for op and use remove/add to decide + +2.9 +Add scheduling options: strict, relaxed, loose (determines user space queueing) +Allow users to specify message types to be kept for logging +Allow users to specify fields to be kept for logging +Pretty Print ausearch messages (strace style?) +Look at modifying kernel rule matcher to do: first match & match all diff --git a/framework/src/audit/audisp/Makefile.am b/framework/src/audit/audisp/Makefile.am new file mode 100644 index 00000000..f5134e52 --- /dev/null +++ b/framework/src/audit/audisp/Makefile.am @@ -0,0 +1,40 @@ +# Makefile.am-- +# Copyright 2007,2011,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +SUBDIRS = plugins +CONFIG_CLEAN_FILES = *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib +sbin_PROGRAMS = audispd +noinst_HEADERS = audispd-config.h audispd-pconfig.h audispd-llist.h \ + queue.h audispd-builtins.h +LIBS = -L${top_builddir}/src/mt -lauditmt +LDADD = -lpthread +AM_CFLAGS = -D_REENTRANT + +audispd_SOURCES = audispd.c audispd-config.c audispd-pconfig.c \ + audispd-llist.c queue.c audispd-builtins.c +audispd_CFLAGS = -fPIE -DPIE -g -D_GNU_SOURCE +audispd_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now + +install-exec-hook: + chmod 0750 $(DESTDIR)$(sbindir)/audispd diff --git a/framework/src/audit/audisp/audispd-builtins.c b/framework/src/audit/audisp/audispd-builtins.c new file mode 100644 index 00000000..f0da6475 --- /dev/null +++ b/framework/src/audit/audisp/audispd-builtins.c @@ -0,0 +1,330 @@ +/* +* audispd-builtins.c - some common builtin plugins +* Copyright (c) 2007,2010,2013 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audispd-pconfig.h" +#include "audispd-builtins.h" + +// Local data +static volatile int sock = -1, conn = -1; +static int syslog_started = 0, priority; +static char *path = NULL; + +// Local prototypes +static void init_af_unix(const plugin_conf_t *conf); +static void init_syslog(const plugin_conf_t *conf); + + +void start_builtin(plugin_conf_t *conf) +{ + if (strcasecmp("builtin_af_unix", conf->path) == 0) { + conf->type = S_AF_UNIX; + init_af_unix(conf); + } else if (strcasecmp("builtin_syslog", conf->path) == 0) { + conf->type = S_SYSLOG; + init_syslog(conf); + } else + syslog(LOG_ERR, "Unknown builtin %s", conf->path); +} + +void stop_builtin(plugin_conf_t *conf) +{ + if (conf->type == S_AF_UNIX) + destroy_af_unix(); + else if (conf->type == S_SYSLOG) + destroy_syslog(); + else + syslog(LOG_ERR, "Unknown builtin %s", conf->path); +} + +static void af_unix_accept(int fd) +{ + int cmd; + + do { + conn = accept(fd, NULL, NULL); + } while (conn < 0 && errno == EINTR); + + // De-register since this is intended to be one listener + if (conn >= 0) + remove_event(fd); + cmd = fcntl(conn, F_GETFD); + fcntl(conn, F_SETFD, cmd|FD_CLOEXEC); +} + +static int create_af_unix_socket(const char *path, int mode) +{ + struct sockaddr_un addr; + socklen_t len; + int rc, cmd; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + syslog(LOG_ERR, "Couldn't open af_unix socket (%s)", + strerror(errno)); + return -1; + } + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(&addr.sun_path[0], path); + len = sizeof(addr); + rc = bind(sock, (const struct sockaddr *)&addr, len); + if (rc < 0) { + syslog(LOG_ERR, "Couldn't bind af_unix socket (%s)", + strerror(errno)); + destroy_af_unix(); + return -1; + } + if (mode != -1) { + rc = chmod(path, mode); + if (rc < 0) { + syslog(LOG_ERR, "Couldn't chmod %s to %04o (%s)", + path, mode, strerror(errno)); + destroy_af_unix(); + return -1; + } + } + + // Put socket in nonblock mode + cmd = fcntl(sock, F_GETFL); + fcntl(sock, F_SETFL, cmd|FNDELAY); + + // don't leak the descriptor + cmd = fcntl(sock, F_GETFD); + fcntl(sock, F_SETFD, cmd|FD_CLOEXEC); + + // Make socket listening...won't block + (void)listen(sock, 5); + + // Register socket with poll + add_event(sock, af_unix_accept); + return 0; +} + +static void init_af_unix(const plugin_conf_t *conf) +{ + int i = 1, mode = -1; + char *base = NULL; + + // while args + while (conf->args[i]) { + int rc, bad = 0; + + // is all nums - do mode + base = conf->args[i]; + while (*base) { + if (!isdigit(*base)) { + bad = 1; + break; + } + base++; + } + if (!bad) { + errno = 0; + mode = strtoul(conf->args[i], NULL, 8); + if (errno) { + syslog(LOG_ERR, "Error converting %s (%s)", + conf->args[i], strerror(errno)); + mode = -1; + bad = 1; + } else if (path) { + rc = chmod(path, mode); + if (rc < 0) { + syslog(LOG_ERR, + "Couldn't chmod %s to %04o (%s)", + conf->args[i], mode, + strerror(errno)); + destroy_af_unix(); + return; + } + } + } else { + // else check for '/' + base = strchr(conf->args[i], '/'); + if (base) { + // get dirname + DIR *d; + char *dir = strdup(conf->args[i]); + base = dirname(dir); + d = opendir(base); + if (d) { + closedir(d); + unlink(conf->args[i]); + if (create_af_unix_socket( + conf->args[i], mode)<0) { + free(dir); + return; + } + path = strdup(conf->args[i]); + bad = 0; + } else + syslog(LOG_ERR, "Couldn't open %s (%s)", + base, strerror(errno)); + free(dir); + } else + syslog(LOG_ERR, "Malformed path %s", + conf->args[i]); + } + if (bad) { + destroy_af_unix(); + return; + } + i++; + } + syslog(LOG_INFO, "af_unix plugin initialized"); +} + +void send_af_unix_string(const char *s, unsigned int len) +{ + if (sock < 0) + return; + + if (conn >= 0) { + int rc; + do { + rc = write(conn, s, len); + } while (rc < 0 && errno == EINTR); + if (rc < 0 && errno == EPIPE) { + close(conn); + conn = -1; + add_event(sock, af_unix_accept); + } + } +} + +void send_af_unix_binary(event_t *e) +{ + if (sock < 0) + return; + + if (conn >= 0) { + int rc; + struct iovec vec[2]; + + vec[0].iov_base = &e->hdr; + vec[0].iov_len = sizeof(struct audit_dispatcher_header); + vec[1].iov_base = e->data; + vec[1].iov_len = MAX_AUDIT_MESSAGE_LENGTH; + do { + rc = writev(conn, vec, 2); + } while (rc < 0 && errno == EINTR); + if (rc < 0 && errno == EPIPE) { + close(conn); + conn = -1; + add_event(sock, af_unix_accept); + } + } +} + +void destroy_af_unix(void) +{ + if (conn >= 0) { + close(conn); + conn = -1; + } + if (sock >= 0) { + close(sock); + sock = -1; + } + if (path) { + unlink(path); + free(path); + path = NULL; + } +} + +static void init_syslog(const plugin_conf_t *conf) +{ + int i, facility = LOG_USER; + priority = LOG_INFO; + + for (i = 1; i<3; i++) { + if (conf->args[i]) { + if (strcasecmp(conf->args[i], "LOG_DEBUG") == 0) + priority = LOG_DEBUG; + else if (strcasecmp(conf->args[i], "LOG_INFO") == 0) + priority = LOG_INFO; + else if (strcasecmp(conf->args[i], "LOG_NOTICE") == 0) + priority = LOG_NOTICE; + else if (strcasecmp(conf->args[i], "LOG_WARNING") == 0) + priority = LOG_WARNING; + else if (strcasecmp(conf->args[i], "LOG_ERR") == 0) + priority = LOG_ERR; + else if (strcasecmp(conf->args[i], "LOG_CRIT") == 0) + priority = LOG_CRIT; + else if (strcasecmp(conf->args[i], "LOG_ALERT") == 0) + priority = LOG_ALERT; + else if (strcasecmp(conf->args[i], "LOG_EMERG") == 0) + priority = LOG_EMERG; + else if (strcasecmp(conf->args[i], "LOG_LOCAL0") == 0) + facility = LOG_LOCAL0; + else if (strcasecmp(conf->args[i], "LOG_LOCAL1") == 0) + facility = LOG_LOCAL1; + else if (strcasecmp(conf->args[i], "LOG_LOCAL2") == 0) + facility = LOG_LOCAL2; + else if (strcasecmp(conf->args[i], "LOG_LOCAL3") == 0) + facility = LOG_LOCAL3; + else if (strcasecmp(conf->args[i], "LOG_LOCAL4") == 0) + facility = LOG_LOCAL4; + else if (strcasecmp(conf->args[i], "LOG_LOCAL5") == 0) + facility = LOG_LOCAL5; + else if (strcasecmp(conf->args[i], "LOG_LOCAL6") == 0) + facility = LOG_LOCAL6; + else if (strcasecmp(conf->args[i], "LOG_LOCAL7") == 0) + facility = LOG_LOCAL7; + else { + syslog(LOG_ERR, + "Unknown log priority/facility %s", + conf->args[i]); + syslog_started = 0; + return; + } + } + } + syslog(LOG_INFO, "syslog plugin initialized"); + if (facility != LOG_USER) + openlog("audispd", 0, facility); + syslog_started = 1; +} + +void send_syslog(const char *s) +{ + if (syslog_started) + syslog(priority, "%s", s); +} + +void destroy_syslog(void) +{ + syslog_started = 0; +} + diff --git a/framework/src/audit/audisp/audispd-builtins.h b/framework/src/audit/audisp/audispd-builtins.h new file mode 100644 index 00000000..79f43b81 --- /dev/null +++ b/framework/src/audit/audisp/audispd-builtins.h @@ -0,0 +1,43 @@ +/* +* audispd-builtins.h - Minimal linked list library +* Copyright (c) 2007,2013 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUDISPD_BUILTINS_HEADER +#define AUDISPD_BUILTINS_HEADER + +#include "queue.h" + +void start_builtin(plugin_conf_t *conf); +void stop_builtin(plugin_conf_t *conf); +void send_af_unix_string(const char *s, unsigned int len); +void send_af_unix_binary(event_t *e); +void destroy_af_unix(void); +void send_syslog(const char *s); +void destroy_syslog(void); + +typedef void (*poll_callback_ptr)(int fd); +int add_event(int fd, poll_callback_ptr cb); +int remove_event(int fd); + + +#endif + diff --git a/framework/src/audit/audisp/audispd-config.c b/framework/src/audit/audisp/audispd-config.c new file mode 100644 index 00000000..5e1fd0ee --- /dev/null +++ b/framework/src/audit/audisp/audispd-config.c @@ -0,0 +1,507 @@ +/* audispd-config.c -- + * Copyright 2007-08,2010,2014-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "audispd-config.h" +#include "private.h" + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; + const char *option; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(struct nv_pair *, int, daemon_conf_t *); + int max_options; +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file); +static int nv_split(char *buf, struct nv_pair *nv); +static const struct kw_pair *kw_lookup(const char *val); +static int q_depth_parser(struct nv_pair *nv, int line, + daemon_conf_t *config); +static int name_format_parser(struct nv_pair *nv, int line, + daemon_conf_t *config); +static int name_parser(struct nv_pair *nv, int line, + daemon_conf_t *config); +static int overflow_action_parser(struct nv_pair *nv, int line, + daemon_conf_t *config); +static int priority_boost_parser(struct nv_pair *nv, int line, + daemon_conf_t *config); +static int max_restarts_parser(struct nv_pair *nv, int line, + daemon_conf_t *config); +static int sanity_check(daemon_conf_t *config, const char *file); + +static const struct kw_pair keywords[] = +{ + {"q_depth", q_depth_parser, 0 }, + {"name_format", name_format_parser, 0 }, + {"name", name_parser, 0 }, + {"overflow_action", overflow_action_parser, 0 }, + {"priority_boost", priority_boost_parser, 0 }, + {"max_restarts", max_restarts_parser, 0 }, + { NULL, NULL } +}; + +static const struct nv_list node_name_formats[] = +{ + {"none", N_NONE }, + {"hostname", N_HOSTNAME }, + {"fqd", N_FQD }, + {"numeric", N_NUMERIC }, + {"user", N_USER }, + { NULL, 0 } +}; + +static const struct nv_list overflow_actions[] = +{ + {"ignore", O_IGNORE }, + {"syslog", O_SYSLOG }, + {"suspend", O_SUSPEND }, + {"single", O_SINGLE }, + {"halt", O_HALT }, + { NULL, 0 } +}; + +/* + * Set everything to its default value +*/ +void clear_config(daemon_conf_t *config) +{ + config->q_depth = 80; + config->overflow_action = O_SYSLOG; + config->priority_boost = 4; + config->max_restarts = 10; + config->node_name_format = N_NONE; + config->name = NULL; +} + +int load_config(daemon_conf_t *config, const char *file) +{ + int fd, rc, mode, lineno = 1; + struct stat st; + FILE *f; + char buf[160]; + + clear_config(config); + + /* open the file */ + mode = O_RDONLY; + rc = open(file, mode); + if (rc < 0) { + if (errno != ENOENT) { + audit_msg(LOG_ERR, "Error opening %s (%s)", file, + strerror(errno)); + return 1; + } + audit_msg(LOG_WARNING, + "Config file %s doesn't exist, skipping", file); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + if (fstat(fd, &st) < 0) { + audit_msg(LOG_ERR, "Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + audit_msg(LOG_ERR, "Error - %s isn't owned by root", + file); + close(fd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + audit_msg(LOG_ERR, "Error - %s is world writable", + file); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + audit_msg(LOG_ERR, "Error - %s is not a regular file", + file); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + audit_msg(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf, sizeof(buf), &lineno, file)) { + // convert line into name-value pair + const struct kw_pair *kw; + struct nv_pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + audit_msg(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, file); + break; + case 2: // no '=' sign + audit_msg(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, file); + break; + default: // something else went wrong... + audit_msg(LOG_ERR, + "Unknown error for line %d in %s", + lineno, file); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + audit_msg(LOG_ERR, + "Not processing any more lines in %s", file); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + audit_msg(LOG_ERR, + "Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, file); + fclose(f); + return 1; + } + + /* Check number of options */ + if (kw->max_options == 0 && nv.option != NULL) { + audit_msg(LOG_ERR, + "Keyword \"%s\" has invalid option " + "\"%s\" in line %d of %s", + nv.name, nv.option, lineno, file); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(&nv, lineno, config); + if (rc != 0) { + fclose(f); + return 1; // local parser puts message out + } + + lineno++; + } + + fclose(f); + if (lineno > 1) + return sanity_check(config, file); + return 0; +} + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file) +{ + int too_long = 0; + + while (fgets_unlocked(buf, size, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) { + if (!too_long) { + *ptr = 0; + return buf; + } + // Reset and start with the next line + too_long = 0; + *lineno = *lineno + 1; + } else { + // If a line is too long skip it. + // Only output 1 warning + if (!too_long) + audit_msg(LOG_ERR, + "Skipping line %d in %s: too long", + *lineno, file); + too_long = 1; + } + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr, *saved; + + nv->name = NULL; + nv->value = NULL; + nv->option = NULL; + ptr = strtok_r(buf, " ", &saved); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* See if there's an option */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) { + nv->option = ptr; + + /* Make sure there's nothing else */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) + return 1; + } + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int q_depth_parser(struct nv_pair *nv, int line, + daemon_conf_t *config) +{ + const char *ptr = nv->value; + unsigned long i; + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + if (i > 99999) { + audit_msg(LOG_ERR, "q_depth must be 99999 or less"); + return 1; + } + config->q_depth = i; + return 0; + +} + +static int name_format_parser(struct nv_pair *nv, int line, + daemon_conf_t *config) +{ + int i; + + for (i=0; node_name_formats[i].name != NULL; i++) { + if (strcasecmp(nv->value, node_name_formats[i].name) == 0) { + config->node_name_format = node_name_formats[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int name_parser(struct nv_pair *nv, int line, + daemon_conf_t *config) +{ + if (nv->value == NULL) + config->name = NULL; + else + config->name = strdup(nv->value); + return 0; +} + +static int overflow_action_parser(struct nv_pair *nv, int line, + daemon_conf_t *config) +{ + int i; + + for (i=0; overflow_actions[i].name != NULL; i++) { + if (strcasecmp(nv->value, overflow_actions[i].name) == 0) { + config->overflow_action = overflow_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int priority_boost_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "priority_boost_parser called with: %s", + nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range */ + if (i > INT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + config->priority_boost = (unsigned int)i; + return 0; +} + +static int max_restarts_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "max_restarts_parser called with: %s", + nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range */ + if (i > INT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + config->max_restarts = (unsigned int)i; + return 0; +} + +/* + * This function is where we do the integrated check of the audispd config + * options. At this point, all fields have been read. Returns 0 if no + * problems and 1 if problems detected. + */ +static int sanity_check(daemon_conf_t *config, const char *file) +{ + /* Error checking */ + if (config->node_name_format == N_USER && config->name == NULL) { + audit_msg(LOG_ERR, + "Error - node_name_format is user supplied but none given (%s)", + file); + return 1; + } + return 0; +} + +void free_config(daemon_conf_t *config) +{ + free((void *)config->name); +} + diff --git a/framework/src/audit/audisp/audispd-config.h b/framework/src/audit/audisp/audispd-config.h new file mode 100644 index 00000000..77585890 --- /dev/null +++ b/framework/src/audit/audisp/audispd-config.h @@ -0,0 +1,48 @@ +/* audispd-config.h -- + * Copyright 2007-08 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUDISPD_CONFIG_H +#define AUDISPD_CONFIG_H + +#include "libaudit.h" + +typedef enum { O_IGNORE, O_SYSLOG, O_SUSPEND, O_SINGLE, + O_HALT } overflow_action_t; +typedef enum { N_NONE, N_HOSTNAME, N_FQD, N_NUMERIC, N_USER } node_t; + +typedef struct daemon_conf +{ + unsigned int q_depth; + overflow_action_t overflow_action; + unsigned int priority_boost; + unsigned int max_restarts; + node_t node_name_format; + const char *name; +} daemon_conf_t; + +void clear_config(daemon_conf_t *config); +int load_config(daemon_conf_t *config, const char *file); +void free_config(daemon_conf_t *config); + +#endif + diff --git a/framework/src/audit/audisp/audispd-llist.c b/framework/src/audit/audisp/audispd-llist.c new file mode 100644 index 00000000..c30d87b5 --- /dev/null +++ b/framework/src/audit/audisp/audispd-llist.c @@ -0,0 +1,157 @@ +/* +* audispd-llist.c - Minimal linked list library +* Copyright (c) 2007,2013 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include "audispd-llist.h" + +void plist_create(conf_llist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +void plist_last(conf_llist *l) +{ + register lnode* window; + + if (l->head == NULL) + return; + + window = l->head; + while (window->next) + window = window->next; + l->cur = window; +} + +lnode *plist_next(conf_llist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +unsigned int plist_count_active(const conf_llist *l) +{ + register lnode* current; + unsigned int cnt = 0; + + current = l->head; + while (current) { + if (current->p && current->p->active == A_YES) + cnt++; + current=current->next; + } + return cnt; +} + +void plist_append(conf_llist *l, plugin_conf_t *p) +{ + lnode* newnode; + + newnode = malloc(sizeof(lnode)); + + if (p) { + void *pp = malloc(sizeof(struct plugin_conf)); + if (pp) + memcpy(pp, p, sizeof(struct plugin_conf)); + newnode->p = pp; + } else + newnode->p = NULL; + + newnode->next = 0; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +void plist_clear(conf_llist* l) +{ + lnode* nextnode; + register lnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->p); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +void plist_mark_all_unchecked(conf_llist* l) +{ + register lnode* current; + + current = l->head; + while (current) { + if (current->p) + current->p->checked = 0; + current=current->next; + } +} + +lnode *plist_find_unchecked(conf_llist* l) +{ + register lnode* current; + + current = l->head; + while (current) { + if (current->p && current->p->checked == 0) + return current; + current=current->next; + } + return NULL; +} + +lnode *plist_find_name(conf_llist* l, const char *name) +{ + register lnode* current; + + if (name == NULL) + return NULL; + + current = l->head; + while (current) { + if (current->p && current->p->name) { + if (strcmp(current->p->name, name) == 0) + return current; + } + current=current->next; + } + return NULL; +} + diff --git a/framework/src/audit/audisp/audispd-llist.h b/framework/src/audit/audisp/audispd-llist.h new file mode 100644 index 00000000..0ddd69a3 --- /dev/null +++ b/framework/src/audit/audisp/audispd-llist.h @@ -0,0 +1,60 @@ +/* +* audispd-llist.h - Header file for ausearch-conf_llist.c +* Copyright (c) 2007,2013 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUDISP_LIST_HEADER +#define AUDISP_LIST_HEADER + +#include "config.h" +#include +#include "audispd-pconfig.h" + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _lnode{ + plugin_conf_t *p; // The rule from the kernel + struct _lnode *next; // Next node pointer +} lnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + lnode *head; // List head + lnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} conf_llist; + +void plist_create(conf_llist *l); +static inline void plist_first(conf_llist *l) { l->cur = l->head; } +static inline unsigned int plist_count(conf_llist *l) { return l->cnt; } +unsigned int plist_count_active(const conf_llist *l); +void plist_last(conf_llist *l); +lnode *plist_next(conf_llist *l); +static inline lnode *plist_get_cur(conf_llist *l) { return l->cur; } +void plist_append(conf_llist *l, plugin_conf_t *p); +void plist_clear(conf_llist* l); +void plist_mark_all_unchecked(conf_llist* l); +lnode *plist_find_unchecked(conf_llist* l); +lnode *plist_find_name(conf_llist* l, const char *name); + +#endif + diff --git a/framework/src/audit/audisp/audispd-pconfig.c b/framework/src/audit/audisp/audispd-pconfig.c new file mode 100644 index 00000000..4ae1b4d1 --- /dev/null +++ b/framework/src/audit/audisp/audispd-pconfig.c @@ -0,0 +1,516 @@ +/* audispd-pconfig.c -- + * Copyright 2007,2010,2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "audispd-pconfig.h" +#include "private.h" + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; + const char *option; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(struct nv_pair *, int, plugin_conf_t *); + int max_options; +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file); +static int nv_split(char *buf, struct nv_pair *nv); +static const struct kw_pair *kw_lookup(const char *val); +static int active_parser(struct nv_pair *nv, int line, + plugin_conf_t *config); +static int direction_parser(struct nv_pair *nv, int line, + plugin_conf_t *config); +static int path_parser(struct nv_pair *nv, int line, + plugin_conf_t *config); +static int service_type_parser(struct nv_pair *nv, int line, + plugin_conf_t *config); +static int args_parser(struct nv_pair *nv, int line, + plugin_conf_t *config); +static int format_parser(struct nv_pair *nv, int line, + plugin_conf_t *config); +static int sanity_check(plugin_conf_t *config, const char *file); + +static const struct kw_pair keywords[] = +{ + {"active", active_parser, 0 }, + {"direction", direction_parser, 0 }, + {"path", path_parser, 0 }, + {"type", service_type_parser, 0 }, + {"args", args_parser, 2 }, + {"format", format_parser, 0 }, + { NULL, NULL } +}; + +static const struct nv_list active[] = +{ + {"yes", A_YES }, + {"no", A_NO }, + { NULL, 0 } +}; + +static const struct nv_list directions[] = +{ +// {"in", D_IN }, FIXME: not supported yet + {"out", D_OUT }, + { NULL, 0 } +}; + +static const struct nv_list service_type[] = +{ + {"builtin", S_BUILTIN }, + {"always", S_ALWAYS }, + { NULL, 0 } +}; + +static const struct nv_list formats[] = +{ + {"binary", F_BINARY }, + {"string", F_STRING }, + { NULL, 0 } +}; + +/* + * Set everything to its default value +*/ +void clear_pconfig(plugin_conf_t *config) +{ + int i; + + config->active = A_NO; + config->direction = D_UNSET; + config->path = NULL; + config->type = S_ALWAYS; + for (i=0; i< (MAX_PLUGIN_ARGS + 2); i++) + config->args[i] = NULL; + config->format = F_STRING; + config->plug_pipe[0] = -1; + config->plug_pipe[1] = -1; + config->pid = 0; + config->inode = 0; + config->checked = 0; + config->name = NULL; + config->restart_cnt = 0; +} + +int load_pconfig(plugin_conf_t *config, char *file) +{ + int fd, rc, mode, lineno = 1; + struct stat st; + FILE *f; + char buf[160]; + + clear_pconfig(config); + + /* open the file */ + mode = O_RDONLY; + rc = open(file, mode); + if (rc < 0) { + if (errno != ENOENT) { + audit_msg(LOG_ERR, "Error opening %s (%s)", file, + strerror(errno)); + return 1; + } + audit_msg(LOG_WARNING, + "Config file %s doesn't exist, skipping", file); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + if (fstat(fd, &st) < 0) { + audit_msg(LOG_ERR, "Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + audit_msg(LOG_ERR, "Error - %s isn't owned by root", + file); + close(fd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + audit_msg(LOG_ERR, "Error - %s is world writable", + file); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + audit_msg(LOG_ERR, "Error - %s is not a regular file", + file); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + audit_msg(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf, sizeof(buf), &lineno, file)) { + // convert line into name-value pair + const struct kw_pair *kw; + struct nv_pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + audit_msg(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, file); + break; + case 2: // no '=' sign + audit_msg(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, file); + break; + default: // something else went wrong... + audit_msg(LOG_ERR, + "Unknown error for line %d in %s", + lineno, file); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + audit_msg(LOG_ERR, + "Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, file); + fclose(f); + return 1; + } + + /* Check number of options */ + if (kw->max_options == 0 && nv.option != NULL) { + audit_msg(LOG_ERR, + "Keyword \"%s\" has invalid option " + "\"%s\" in line %d of %s", + nv.name, nv.option, lineno, file); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(&nv, lineno, config); + if (rc != 0) { + fclose(f); + return 1; // local parser puts message out + } + + lineno++; + } + + fclose(f); + config->name = strdup(basename(file)); + if (lineno > 1) + return sanity_check(config, file); + return 0; +} + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file) +{ + int too_long = 0; + + while (fgets_unlocked(buf, size, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) { + if (!too_long) { + *ptr = 0; + return buf; + } + // Reset and start with the next line + too_long = 0; + *lineno = *lineno + 1; + } else { + // If a line is too long skip it. + // Only output 1 warning + if (!too_long) + audit_msg(LOG_ERR, + "Skipping line %d in %s: too long", + *lineno, file); + too_long = 1; + } + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr, *saved; + + nv->name = NULL; + nv->value = NULL; + nv->option = NULL; + ptr = strtok_r(buf, " ", &saved); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* See if there's an option */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) { + nv->option = ptr; + + /* Make sure there's nothing else */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) + return 1; + } + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int active_parser(struct nv_pair *nv, int line, + plugin_conf_t *config) +{ + int i; + + for (i=0; active[i].name != NULL; i++) { + if (strcasecmp(nv->value, active[i].name) == 0) { + config->active = active[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int direction_parser(struct nv_pair *nv, int line, + plugin_conf_t *config) +{ + int i; + + for (i=0; directions[i].name != NULL; i++) { + if (strcasecmp(nv->value, directions[i].name) == 0) { + config->direction = directions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int path_parser(struct nv_pair *nv, int line, + plugin_conf_t *config) +{ + char *dir = NULL, *tdir; + struct stat buf; + + if (nv->value == NULL) { + config->path = NULL; + return 0; + } + + if (strncasecmp(nv->value, "builtin_", 8) == 0) { + config->path = strdup(nv->value); + return 0; + } + + /* get dir form name. */ + tdir = strdup(nv->value); + if (tdir) + dir = dirname(tdir); + if (dir == NULL || strlen(dir) < 4) { // '/var' is shortest dirname + audit_msg(LOG_ERR, + "The directory name: %s is too short - line %d", + dir, line); + free(tdir); + return 1; + } + + free((void *)tdir); + /* If the file exists, see that its regular, owned by root, + * and not world anything */ + if (stat(nv->value, &buf) < 0) { + audit_msg(LOG_ERR, "Unable to stat %s (%s)", nv->value, + strerror(errno)); + return 1; + } + if (!S_ISREG(buf.st_mode)) { + audit_msg(LOG_ERR, "%s is not a regular file", nv->value); + return 1; + } + if (buf.st_uid != 0) { + audit_msg(LOG_ERR, "%s is not owned by root", nv->value); + return 1; + } + if ((buf.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP)) != + (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP)) { + audit_msg(LOG_ERR, "%s permissions should be 0750", nv->value); + return 1; + } + free((void *)config->path); + config->path = strdup(nv->value); + config->inode = buf.st_ino; + if (config->path == NULL) + return 1; + return 0; +} + +static int service_type_parser(struct nv_pair *nv, int line, + plugin_conf_t *config) +{ + int i; + + for (i=0; service_type[i].name != NULL; i++) { + if (strcasecmp(nv->value, service_type[i].name) == 0) { + config->type = service_type[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int args_parser(struct nv_pair *nv, int line, + plugin_conf_t *config) +{ + int i; + + for (i=0; i < (MAX_PLUGIN_ARGS + 2); i++) { + free((void *)config->args[i]); + config->args[i] = NULL; + } + + config->args[1] = strdup(nv->value); + if (nv->option) + config->args[2] = strdup(nv->option); + return 0; +} + +static int format_parser(struct nv_pair *nv, int line, + plugin_conf_t *config) +{ + int i; + + for (i=0; formats[i].name != NULL; i++) { + if (strcasecmp(nv->value, formats[i].name) == 0) { + config->format = formats[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +/* + * This function is where we do the integrated check of the audispd config + * options. At this point, all fields have been read. Returns 0 if no + * problems and 1 if problems detected. + */ +static int sanity_check(plugin_conf_t *config, const char *file) +{ + /* Error checking */ + if (config->active == A_YES && config->path == NULL) { + audit_msg(LOG_ERR, + "Error - plugin (%s) is active but no path given", file); + return 1; + } + return 0; +} + +void free_pconfig(plugin_conf_t *config) +{ + int i; + + if (config == NULL) + return; + + for (i=0; i < (MAX_PLUGIN_ARGS + 2); i++) + free(config->args[i]); + if (config->plug_pipe[0] >= 0) + close(config->plug_pipe[0]); + if (config->plug_pipe[1] >= 0) + close(config->plug_pipe[1]); + free((void *)config->path); + free((void *)config->name); +} + diff --git a/framework/src/audit/audisp/audispd-pconfig.h b/framework/src/audit/audisp/audispd-pconfig.h new file mode 100644 index 00000000..05216352 --- /dev/null +++ b/framework/src/audit/audisp/audispd-pconfig.h @@ -0,0 +1,57 @@ +/* audispd-pconfig.h -- + * Copyright 2007,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUDISPD_PCONFIG_H +#define AUDISPD_PCONFIG_H + +#include +#include "libaudit.h" +#define MAX_PLUGIN_ARGS 2 + +typedef enum { A_NO, A_YES } active_t; +typedef enum { D_UNSET, D_IN, D_OUT } direction_t; +typedef enum { S_ALWAYS, S_BUILTIN, S_AF_UNIX, S_SYSLOG } service_t; +typedef enum { F_BINARY, F_STRING } format_t; + +typedef struct plugin_conf +{ + active_t active; /* Current state - active or not */ + direction_t direction; /* in or out kind of plugin */ + const char *path; /* path to binary */ + service_t type; /* builtin or always */ + char *args[MAX_PLUGIN_ARGS+2]; /* args to be passed to plugin */ + format_t format; /* Event format */ + int plug_pipe[2]; /* Communication pipe for events */ + pid_t pid; /* Used to signal children */ + ino_t inode; /* Use to see if new binary was installed */ + int checked; /* Used for internal housekeeping on HUP */ + char *name; /* Used to distinguish plugins for HUP */ + unsigned restart_cnt; /* Number of times its crashed */ +} plugin_conf_t; + +void clear_pconfig(plugin_conf_t *config); +int load_pconfig(plugin_conf_t *config, char *file); +void free_pconfig(plugin_conf_t *config); + +#endif + diff --git a/framework/src/audit/audisp/audispd.c b/framework/src/audit/audisp/audispd.c new file mode 100644 index 00000000..06494b1e --- /dev/null +++ b/framework/src/audit/audisp/audispd.c @@ -0,0 +1,854 @@ +/* audispd.c -- + * Copyright 2007-08,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audispd-config.h" +#include "audispd-pconfig.h" +#include "audispd-llist.h" +#include "audispd-builtins.h" +#include "queue.h" +#include "libaudit.h" + +/* Global Data */ +volatile int stop = 0; +volatile int hup = 0; + +/* Local data */ +static daemon_conf_t daemon_config; +static conf_llist plugin_conf; +static int audit_fd; +static pthread_t inbound_thread; +static const char *config_file = "/etc/audisp/audispd.conf"; +static const char *plugin_dir = "/etc/audisp/plugins.d/"; + +/* Local function prototypes */ +static void signal_plugins(int sig); +static int event_loop(void); +static int safe_exec(plugin_conf_t *conf); +static void *inbound_thread_main(void *arg); +static void process_inbound_event(int fd); + +/* + * SIGTERM handler + */ +static void term_handler( int sig ) +{ + stop = 1; +} + +/* + * SIGCHLD handler + */ +static void child_handler( int sig ) +{ + int status; + pid_t pid; + + pid = waitpid(-1, &status, WNOHANG); + if (pid > 0) { + // Mark the child pid as 0 in the configs + lnode *tpconf; + plist_first(&plugin_conf); + tpconf = plist_get_cur(&plugin_conf); + while (tpconf) { + if (tpconf->p && tpconf->p->pid == pid) { + tpconf->p->pid = 0; + break; + } + tpconf = plist_next(&plugin_conf); + } + } +} + +/* + * SIGHUP handler: re-read config + */ +static void hup_handler( int sig ) +{ + hup = 1; +} + +/* + * SIGALRM handler - help force exit when terminating daemon + */ +static void alarm_handler( int sig ) +{ + pthread_cancel(inbound_thread); + raise(SIGTERM); +} + +static int count_dots(const char *s) +{ + const char *ptr; + int cnt = 0; + + while ((ptr = strchr(s, '.'))) { + cnt++; + s = ptr + 1; + } + return cnt; +} + +static void load_plugin_conf(conf_llist *plugin) +{ + DIR *d; + + /* init plugin list */ + plist_create(plugin); + + /* read configs */ + d = opendir(plugin_dir); + if (d) { + struct dirent *e; + + while ((e = readdir(d))) { + plugin_conf_t config; + char fname[PATH_MAX]; + + // Don't run backup files, hidden files, or dirs + if (e->d_name[0] == '.' || count_dots(e->d_name) > 1) + continue; + + snprintf(fname, sizeof(fname), "%s%s", + plugin_dir, e->d_name); + + clear_pconfig(&config); + if (load_pconfig(&config, fname) == 0) { + /* Push onto config list only if active */ + if (config.active == A_YES) + plist_append(plugin, &config); + else + free_pconfig(&config); + } else + syslog(LOG_ERR, + "Skipping %s plugin due to errors", + e->d_name); + } + closedir(d); + } +} + +static int start_one_plugin(lnode *conf) +{ + if (conf->p->restart_cnt > daemon_config.max_restarts) + return 1; + + if (conf->p->type == S_BUILTIN) + start_builtin(conf->p); + else if (conf->p->type == S_ALWAYS) { + if (safe_exec(conf->p)) { + syslog(LOG_ERR, + "Error running %s (%s) continuing without it", + conf->p->path, strerror(errno)); + conf->p->active = A_NO; + return 0; + } + + /* Close the parent's read side */ + close(conf->p->plug_pipe[0]); + conf->p->plug_pipe[0] = -1; + /* Avoid leaking descriptor */ + fcntl(conf->p->plug_pipe[1], F_SETFD, FD_CLOEXEC); + } + return 1; +} + +static int start_plugins(conf_llist *plugin) +{ + /* spawn children */ + lnode *conf; + int active = 0; + + plist_first(plugin); + conf = plist_get_cur(plugin); + if (conf == NULL || conf->p == NULL) + return active; + + do { + if (conf->p && conf->p->active == A_YES) { + if (start_one_plugin(conf)) + active++; + } + } while ((conf = plist_next(plugin))); + return active; +} + +static int reconfigure(void) +{ + int rc; + daemon_conf_t tdc; + conf_llist tmp_plugin; + lnode *tpconf; + + /* Read new daemon config */ + rc = load_config(&tdc, config_file); + if (rc == 0) { + if (tdc.q_depth > daemon_config.q_depth) { + increase_queue_depth(tdc.q_depth); + daemon_config.q_depth = tdc.q_depth; + } + daemon_config.overflow_action = tdc.overflow_action; + reset_suspended(); + /* We just fill these in because they are used by this + * same thread when we return + */ + daemon_config.node_name_format = tdc.node_name_format; + free((char *)daemon_config.name); + daemon_config.name = tdc.name; + } + + /* The idea for handling SIGHUP to children goes like this: + * 1) load the current config in temp list + * 2) mark all in real list unchecked + * 3) for each one in tmp list, scan old list + * 4) if new, start it, append to list, mark done + * 5) else check if there was a change to active state + * 6) if so, copy config over and start + * 7) If no change, send sighup to non-builtins and mark done + * 8) Finally, scan real list for unchecked, terminate and deactivate + */ + syslog(LOG_INFO, "Starting reconfigure"); + load_plugin_conf(&tmp_plugin); + plist_mark_all_unchecked(&plugin_conf); + + plist_first(&tmp_plugin); + tpconf = plist_get_cur(&tmp_plugin); + while (tpconf && tpconf->p) { + lnode *opconf; + + opconf = plist_find_name(&plugin_conf, tpconf->p->name); + if (opconf == NULL) { + /* We have a new service */ + if (tpconf->p->active == A_YES) { + tpconf->p->checked = 1; + plist_last(&plugin_conf); + plist_append(&plugin_conf, tpconf->p); + free(tpconf->p); + tpconf->p = NULL; + start_one_plugin(plist_get_cur(&plugin_conf)); + } + } else { + if (opconf->p->active == tpconf->p->active) { + /* If active and no state change, sighup it */ + if (opconf->p->type == S_ALWAYS && + opconf->p->active == A_YES) { + if (opconf->p->inode==tpconf->p->inode) + kill(opconf->p->pid, SIGHUP); + else { + /* Binary changed, restart */ + syslog(LOG_INFO, + "Restarting %s since binary changed", + opconf->p->path); + kill(opconf->p->pid, SIGTERM); + usleep(50000); // 50 msecs + close(opconf->p->plug_pipe[1]); + opconf->p->plug_pipe[1] = -1; + opconf->p->pid = 0; + start_one_plugin(opconf); + opconf->p->inode = + tpconf->p->inode; + } + } + opconf->p->checked = 1; + } else { + /* A change in state */ + if (tpconf->p->active == A_YES) { + /* starting - copy config and exec */ + free_pconfig(opconf->p); + free(opconf->p); + opconf->p = tpconf->p; + opconf->p->checked = 1; + start_one_plugin(opconf); + tpconf->p = NULL; + } + } + } + + tpconf = plist_next(&tmp_plugin); + } + + /* Now see what's left over */ + while ( (tpconf = plist_find_unchecked(&plugin_conf)) ) { + /* Anything not checked is something removed from the config */ + tpconf->p->active = A_NO; + syslog(LOG_INFO, "Terminating %s because its now inactive", + tpconf->p->path); + if (tpconf->p->type == S_ALWAYS) { + kill(tpconf->p->pid, SIGTERM); + close(tpconf->p->plug_pipe[1]); + } else + stop_builtin(tpconf->p); + tpconf->p->plug_pipe[1] = -1; + tpconf->p->pid = 0; + tpconf->p->checked = 1; + } + + /* Release memory from temp config */ + plist_first(&tmp_plugin); + tpconf = plist_get_cur(&tmp_plugin); + while (tpconf) { + free_pconfig(tpconf->p); + tpconf = plist_next(&tmp_plugin); + } + plist_clear(&tmp_plugin); + return plist_count_active(&plugin_conf); +} + +int main(int argc, char *argv[]) +{ + lnode *conf; + struct sigaction sa; + int i; + +#ifndef DEBUG + /* Make sure we are root */ + if (getuid() != 0) { + fprintf(stderr, "You must be root to run this program.\n"); + return 4; + } +#endif + set_aumessage_mode(MSG_SYSLOG, DBG_YES); + + /* Clear any procmask set by libev */ + sigfillset (&sa.sa_mask); + sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); + + /* Register sighandlers */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + /* Ignore all signals by default */ + sa.sa_handler = SIG_IGN; + for (i=1; i= 0) { + if (dup2(0, i) < 0 || dup2(1, i) < 0 || dup2(2, i) < 0) { + syslog(LOG_ERR, "Failed duping /dev/null %s, exiting", + strerror(errno)); + return 1; + } + close(i); + } else { + syslog(LOG_ERR, "Failed opening /dev/null %s, exiting", + strerror(errno)); + return 1; + } + if (fcntl(audit_fd, F_SETFD, FD_CLOEXEC) < 0) { + syslog(LOG_ERR, "Failed protecting input %s, exiting", + strerror(errno)); + return 1; + } + + /* init the daemon's config */ + if (load_config(&daemon_config, config_file)) + return 6; + + load_plugin_conf(&plugin_conf); + + /* if no plugins - exit */ + if (plist_count(&plugin_conf) == 0) { + syslog(LOG_NOTICE, "No plugins found, exiting"); + return 0; + } + + /* Plugins are started with the auditd priority */ + i = start_plugins(&plugin_conf); + + /* Now boost priority to make sure we are getting time slices */ + if (daemon_config.priority_boost != 0) { + int rc; + + errno = 0; + rc = nice((int)-daemon_config.priority_boost); + if (rc == -1 && errno) { + syslog(LOG_ERR, "Cannot change priority (%s)", + strerror(errno)); + /* Stay alive as this is better than stopping */ + } + } + + /* Let the queue initialize */ + init_queue(daemon_config.q_depth); + syslog(LOG_INFO, + "audispd initialized with q_depth=%d and %d active plugins", + daemon_config.q_depth, i); + + /* Tell it to poll the audit fd */ + if (add_event(audit_fd, process_inbound_event) < 0) { + syslog(LOG_ERR, "Cannot add event, exiting"); + return 1; + } + + /* Create inbound thread */ + pthread_create(&inbound_thread, NULL, inbound_thread_main, NULL); + + /* Start event loop */ + while (event_loop()) { + hup = 0; + if (reconfigure() == 0) { + syslog(LOG_INFO, + "After reconfigure, there are no active plugins, exiting"); + break; + } + } + + /* Tell plugins we are going down */ + signal_plugins(SIGTERM); + + /* Cleanup builtin plugins */ + destroy_af_unix(); + destroy_syslog(); + + /* Give it 5 seconds to clear the queue */ + alarm(5); + pthread_join(inbound_thread, NULL); + + /* Release configs */ + plist_first(&plugin_conf); + conf = plist_get_cur(&plugin_conf); + while (conf) { + free_pconfig(conf->p); + conf = plist_next(&plugin_conf); + } + plist_clear(&plugin_conf); + + /* Cleanup the queue */ + destroy_queue(); + free_config(&daemon_config); + + return 0; +} + +static int safe_exec(plugin_conf_t *conf) +{ + char *argv[MAX_PLUGIN_ARGS+2]; + int pid, i; + + /* Set up IPC with child */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, conf->plug_pipe) != 0) + return -1; + + pid = fork(); + if (pid > 0) { + conf->pid = pid; + return 0; /* Parent...normal exit */ + } + if (pid < 0) { + close(conf->plug_pipe[0]); + close(conf->plug_pipe[1]); + conf->pid = 0; + return -1; /* Failed to fork */ + } + + /* Set up comm with child */ + dup2(conf->plug_pipe[0], 0); + for (i=3; i<24; i++) /* Arbitrary number */ + close(i); + + /* Child */ + argv[0] = (char *)conf->path; + for (i=1; i<(MAX_PLUGIN_ARGS+1); i++) + argv[i] = conf->args[i]; + argv[i] = NULL; + execve(conf->path, argv, NULL); + exit(1); /* Failed to exec */ +} + +static void signal_plugins(int sig) +{ + lnode *conf; + + plist_first(&plugin_conf); + conf = plist_get_cur(&plugin_conf); + while (conf) { + if (conf->p && conf->p->pid && conf->p->type == S_ALWAYS) + kill(conf->p->pid, sig); + conf = plist_next(&plugin_conf); + } +} + +static int write_to_plugin(event_t *e, const char *string, size_t string_len, + lnode *conf) +{ + int rc; + + if (conf->p->format == F_STRING) { + do { + rc = write(conf->p->plug_pipe[1], string, string_len); + } while (rc < 0 && errno == EINTR); + } else { + struct iovec vec[2]; + + vec[0].iov_base = &e->hdr; + vec[0].iov_len = sizeof(struct audit_dispatcher_header); + + vec[1].iov_base = e->data; + vec[1].iov_len = MAX_AUDIT_MESSAGE_LENGTH; + do { + rc = writev(conf->p->plug_pipe[1], vec, 2); + } while (rc < 0 && errno == EINTR); + } + return rc; +} + +/* Returns 0 on stop, and 1 on HUP */ +static int event_loop(void) +{ + char *name = NULL, tmp_name[255]; + + /* Get the host name representation */ + switch (daemon_config.node_name_format) + { + case N_NONE: + break; + case N_HOSTNAME: + if (gethostname(tmp_name, sizeof(tmp_name))) { + syslog(LOG_ERR, "Unable to get machine name"); + name = strdup("?"); + } else + name = strdup(tmp_name); + break; + case N_USER: + if (daemon_config.name) + name = strdup(daemon_config.name); + else { + syslog(LOG_ERR, "User defined name missing"); + name = strdup("?"); + } + break; + case N_FQD: + if (gethostname(tmp_name, sizeof(tmp_name))) { + syslog(LOG_ERR, "Unable to get machine name"); + name = strdup("?"); + } else { + int rc; + struct addrinfo *ai; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + + rc = getaddrinfo(tmp_name, NULL, &hints, &ai); + if (rc != 0) { + syslog(LOG_ERR, + "Cannot resolve hostname %s (%s)", + tmp_name, gai_strerror(rc)); + name = strdup("?"); + break; + } + name = strdup(ai->ai_canonname); + freeaddrinfo(ai); + } + break; + case N_NUMERIC: + if (gethostname(tmp_name, sizeof(tmp_name))) { + syslog(LOG_ERR, "Unable to get machine name"); + name = strdup("?"); + } else { + int rc; + struct addrinfo *ai; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + + rc = getaddrinfo(tmp_name, NULL, &hints, &ai); + if (rc != 0) { + syslog(LOG_ERR, + "Cannot resolve hostname %s (%s)", + tmp_name, gai_strerror(rc)); + name = strdup("?"); + break; + } + inet_ntop(ai->ai_family, + ai->ai_family == AF_INET ? + (void *) &((struct sockaddr_in *)ai->ai_addr)->sin_addr : + (void *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, + tmp_name, INET6_ADDRSTRLEN); + freeaddrinfo(ai); + name = strdup(tmp_name); + } + break; + } + + /* Figure out the format for the af_unix socket */ + while (stop == 0) { + event_t *e; + const char *type; + char *v, *ptr, unknown[32]; + unsigned int len; + lnode *conf; + + /* This is where we block until we have an event */ + e = dequeue(); + if (e == NULL) { + if (hup) { + free(name); + return 1; + } + continue; + } + + /* Get the event formatted */ + type = audit_msg_type_to_name(e->hdr.type); + if (type == NULL) { + snprintf(unknown, sizeof(unknown), + "UNKNOWN[%d]", e->hdr.type); + type = unknown; + } + + if (daemon_config.node_name_format != N_NONE) { + len = asprintf(&v, "node=%s type=%s msg=%.*s\n", + name, type, e->hdr.size, e->data); + } else + len = asprintf(&v, "type=%s msg=%.*s\n", + type, e->hdr.size, e->data); + if (len <= 0) { + v = NULL; + free(e); /* Either corrupted event or no memory */ + continue; + } + + /* Strip newlines from event record */ + ptr = v; + while ((ptr = strchr(ptr, 0x0A)) != NULL) { + if (ptr != &v[len-1]) + *ptr = ' '; + else + break; /* Done - exit loop */ + } + + /* Distribute event to the plugins */ + plist_first(&plugin_conf); + conf = plist_get_cur(&plugin_conf); + do { + if (conf == NULL || conf->p == NULL) + continue; + if (conf->p->active == A_NO || stop) + continue; + + /* Now send the event to the right child */ + if (conf->p->type == S_SYSLOG) + send_syslog(v); + else if (conf->p->type == S_AF_UNIX) { + if (conf->p->format == F_STRING) + send_af_unix_string(v, len); + else + send_af_unix_binary(e); + } else if (conf->p->type == S_ALWAYS && !stop) { + int rc; + rc = write_to_plugin(e, v, len, conf); + if (rc < 0 && errno == EPIPE) { + /* Child disappeared ? */ + syslog(LOG_ERR, + "plugin %s terminated unexpectedly", + conf->p->path); + conf->p->pid = 0; + conf->p->restart_cnt++; + if (conf->p->restart_cnt > + daemon_config.max_restarts) { + syslog(LOG_ERR, + "plugin %s has exceeded max_restarts", + conf->p->path); + } + close(conf->p->plug_pipe[1]); + conf->p->plug_pipe[1] = -1; + conf->p->active = A_NO; + if (!stop && start_one_plugin(conf)) { + rc = write_to_plugin(e, v, len, + conf); + syslog(LOG_NOTICE, + "plugin %s was restarted", + conf->p->path); + conf->p->active = A_YES; + } + } + } + } while (!stop && (conf = plist_next(&plugin_conf))); + + /* Done with the memory...release it */ + free(v); + free(e); + if (hup) + break; + } + free(name); + if (stop) + return 0; + else + return 1; +} + +static struct pollfd pfd[4]; +static poll_callback_ptr pfd_cb[4]; +static volatile int pfd_cnt=0; +int add_event(int fd, poll_callback_ptr cb) +{ + if (pfd_cnt > 3) + return -1; + + pfd[pfd_cnt].fd = fd; + pfd[pfd_cnt].events = POLLIN; + pfd[pfd_cnt].revents = 0; + pfd_cb[pfd_cnt] = cb; + pfd_cnt++; + return 0; +} + +int remove_event(int fd) +{ + int start, i; + if (pfd_cnt == 0) + return -1; + + for (start=0; start < pfd_cnt; start++) { + if (pfd[start].fd == fd) + break; + } + for (i=start; i<(pfd_cnt-1); i++) { + pfd[i].events = pfd[i+1].events; + pfd[i].revents = pfd[i+1].revents; + pfd[i].fd = pfd[i+1].fd; + pfd_cb[i] = pfd_cb[i+1]; + } + + pfd_cnt--; + return 0; +} + +/* inbound thread - enqueue inbound data to intermediate table */ +static void *inbound_thread_main(void *arg) +{ + while (stop == 0) { + int rc; + if (hup) + nudge_queue(); + do { + rc = poll(pfd, pfd_cnt, 20000); /* 20 sec */ + } while (rc < 0 && errno == EAGAIN && stop == 0 && hup == 0); + if (rc == 0) + continue; + + /* Event readable... */ + if (rc > 0) { + /* Figure out the fd that is ready and call */ + int i = 0; + while (i < pfd_cnt) { + if (pfd[i].revents & POLLIN) + pfd_cb[i](pfd[i].fd); + i++; + } + } + } + /* make sure event loop wakes up */ + nudge_queue(); + return NULL; +} + +static void process_inbound_event(int fd) +{ + int rc; + struct iovec vec; + event_t *e = malloc(sizeof(event_t)); + if (e == NULL) + return; + memset(e, 0, sizeof(event_t)); + + /* Get header first. It is fixed size */ + vec.iov_base = &e->hdr; + vec.iov_len = sizeof(struct audit_dispatcher_header); + do { + rc = readv(fd, &vec, 1); + } while (rc < 0 && errno == EINTR); + + if (rc <= 0) { + if (rc == 0) + stop = 1; // End of File + free(e); + return; + } + + if (rc > 0) { + /* Sanity check */ + if (e->hdr.ver != AUDISP_PROTOCOL_VER || + e->hdr.hlen != sizeof(e->hdr) || + e->hdr.size > MAX_AUDIT_MESSAGE_LENGTH) { + free(e); + syslog(LOG_ERR, + "Dispatcher protocol mismatch, exiting"); + exit(1); + } + + /* Next payload */ + vec.iov_base = e->data; + vec.iov_len = e->hdr.size; + do { + rc = readv(fd, &vec, 1); + } while (rc < 0 && errno == EINTR); + + if (rc > 0) + enqueue(e, &daemon_config); + else { + if (rc == 0) + stop = 1; // End of File + free(e); + } + } +} + diff --git a/framework/src/audit/audisp/plugins/Makefile.am b/framework/src/audit/audisp/plugins/Makefile.am new file mode 100644 index 00000000..2cba14b8 --- /dev/null +++ b/framework/src/audit/audisp/plugins/Makefile.am @@ -0,0 +1,32 @@ +# Makefile.am -- +# Copyright 2007-08 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig + +SUBDIRS = builtins remote +if ENABLE_ZOS_REMOTE +SUBDIRS += zos-remote +endif +if HAVE_PRELUDE +SUBDIRS += prelude +endif + diff --git a/framework/src/audit/audisp/plugins/builtins/Makefile.am b/framework/src/audit/audisp/plugins/builtins/Makefile.am new file mode 100644 index 00000000..713dee86 --- /dev/null +++ b/framework/src/audit/audisp/plugins/builtins/Makefile.am @@ -0,0 +1,39 @@ +# Makefile.am-- +# Copyright 2007 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.rej *.orig +CONF_FILES = af_unix.conf syslog.conf +EXTRA_DIST = $(CONF_FILES) +plugin_confdir=$(sysconfdir)/audisp/plugins.d + +install-data-hook: + mkdir -p -m 0750 ${DESTDIR}${plugin_confdir} + for i in $(CONF_FILES); do \ + $(INSTALL_DATA) -D -m 640 ${srcdir}/"$$i" \ + ${DESTDIR}${plugin_confdir}; \ + done + +uninstall-hook: + for i in $(CONF_FILES); do \ + rm ${DESTDIR}${plugin_confdir}/"$$i"; \ + done + diff --git a/framework/src/audit/audisp/plugins/builtins/af_unix.conf b/framework/src/audit/audisp/plugins/builtins/af_unix.conf new file mode 100644 index 00000000..a5ba8b1f --- /dev/null +++ b/framework/src/audit/audisp/plugins/builtins/af_unix.conf @@ -0,0 +1,14 @@ + +# This file controls the configuration of the +# af_unix socket plugin. It simply takes events +# and writes them to a unix domain socket. This +# plugin can take 2 arguments, the path for the +# socket and the socket permissions in octal. + +active = no +direction = out +path = builtin_af_unix +type = builtin +args = 0640 /var/run/audispd_events +format = string + diff --git a/framework/src/audit/audisp/plugins/builtins/syslog.conf b/framework/src/audit/audisp/plugins/builtins/syslog.conf new file mode 100644 index 00000000..d603b2f2 --- /dev/null +++ b/framework/src/audit/audisp/plugins/builtins/syslog.conf @@ -0,0 +1,13 @@ +# This file controls the configuration of the syslog plugin. +# It simply takes events and writes them to syslog. The +# arguments provided can be the default priority that you +# want the events written with. And optionally, you can give +# a second argument indicating the facility that you want events +# logged to. Valid options are LOG_LOCAL0 through 7. + +active = no +direction = out +path = builtin_syslog +type = builtin +args = LOG_INFO +format = string diff --git a/framework/src/audit/audisp/plugins/prelude/Makefile.am b/framework/src/audit/audisp/plugins/prelude/Makefile.am new file mode 100644 index 00000000..a70d7652 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/Makefile.am @@ -0,0 +1,50 @@ +# Makefile.am -- +# Copyright 2008-09,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.rej *.orig +EXTRA_DIST = au-prelude.conf audisp-prelude.conf +AUTOMAKE_OPTIONS = no-dependencies +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib -I${top_srcdir}/auparse +LIBS = -L${top_builddir}/auparse/.libs -lauparse -lprelude +LDADD = -lpthread $(CAPNG_LDADD) +prog_confdir = $(sysconfdir)/audisp +prog_conf = audisp-prelude.conf +plugin_confdir=$(prog_confdir)/plugins.d +plugin_conf = au-prelude.conf +sbin_PROGRAMS = audisp-prelude +noinst_HEADERS = prelude-config.h audisp-int.h +dist_man_MANS = audisp-prelude.8 audisp-prelude.conf.5 + +audisp_prelude_SOURCES = audisp-prelude.c prelude-config.c audisp-int.c +audisp_prelude_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE -Wundef \ + @LIBPRELUDE_CFLAGS@ +audisp_prelude_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now @LIBPRELUDE_LDFLAGS@ + +install-data-hook: + mkdir -p -m 0750 ${DESTDIR}${plugin_confdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/$(plugin_conf) ${DESTDIR}${plugin_confdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/$(prog_conf) ${DESTDIR}${prog_confdir} + +uninstall-hook: + rm ${DESTDIR}${plugin_confdir}/$(plugin_conf) + rm ${DESTDIR}${prog_confdir}/$(prog_conf) + diff --git a/framework/src/audit/audisp/plugins/prelude/au-prelude.conf b/framework/src/audit/audisp/plugins/prelude/au-prelude.conf new file mode 100644 index 00000000..513fcf91 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/au-prelude.conf @@ -0,0 +1,12 @@ + +# This file controls the audispd data path to the audit +# based prelude IDS (Intrusion Detection System) plugin. It +# watches events and sends intersting ones to the prelude manager. + +active = no +direction = out +path = /sbin/audisp-prelude +type = always +#args = +format = string + diff --git a/framework/src/audit/audisp/plugins/prelude/audisp-int.c b/framework/src/audit/audisp/plugins/prelude/audisp-int.c new file mode 100644 index 00000000..54d7a3df --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/audisp-int.c @@ -0,0 +1,114 @@ +/* +* audisp-int.c - Minimal linked list library for integers +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include "audisp-int.h" + +void ilist_create(ilist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +int_node *ilist_next(ilist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +void ilist_append(ilist *l, int num) +{ + int_node* newnode; + + newnode = malloc(sizeof(int_node)); + + newnode->num = num; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +int ilist_find_num(ilist *l, unsigned int num) +{ + register int_node* window = l->head; + + while (window) { + if (window->num == num) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +void ilist_clear(ilist* l) +{ + int_node* nextnode; + register int_node* current; + + if (l == NULL) + return; + + current = l->head; + while (current) { + nextnode=current->next; + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +int ilist_add_if_uniq(ilist *l, int num) +{ + register int_node* cur; + + cur = l->head; + while (cur) { + if (cur->num == num) + return 0; + else + cur = cur->next; + } + + /* No matches, append to the end */ + ilist_append(l, num); + return 1; +} + diff --git a/framework/src/audit/audisp/plugins/prelude/audisp-int.h b/framework/src/audit/audisp/plugins/prelude/audisp-int.h new file mode 100644 index 00000000..b0204753 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/audisp-int.h @@ -0,0 +1,57 @@ +/* +* audisp-int.h - Header file for audisp-int.c +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUINT_HEADER +#define AUINT_HEADER + +#include "config.h" +#include + +/* This is the node of the linked list. Number & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _int_node{ + int num; // The number + struct _int_node* next; // Next string node pointer +} int_node; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + int_node *head; // List head + int_node *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} ilist; + +void ilist_create(ilist *l); +static inline void ilist_first(ilist *l) { l->cur = l->head; } +int_node *ilist_next(ilist *l); +static inline int_node *ilist_get_cur(ilist *l) { return l->cur; } +void ilist_append(ilist *l, int num); +void ilist_clear(ilist* l); +int ilist_find_num(ilist *l, unsigned int num); + +/* append a number if its not already on the list */ +int ilist_add_if_uniq(ilist *l, int num); + +#endif + diff --git a/framework/src/audit/audisp/plugins/prelude/audisp-prelude.8 b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.8 new file mode 100644 index 00000000..e457407e --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.8 @@ -0,0 +1,72 @@ +.TH AUDISP-PRELUDE: "8" "Dec 2008" "Red Hat" "System Administration Utilities" +.SH NAME +audisp\-prelude \- plugin for IDMEF alerts +.SH SYNOPSIS +.B audisp\-prelude [ \-\-test ] +.SH DESCRIPTION +\fBaudisp\-prelude\fP is a plugin for the audit event dispatcher daemon, audispd, that uses libprelude to send IDMEF alerts for possible Intrusion Detection events. This plugin requires connecting to a prelude\-manager to record the events it sends. This plugin will analyze audit events in realtime and send detected events to the prelude\-manager for correlation, recording, and display. + +Events that are currently supported are: Logins, Forbidden Login Location, Max Concurrent Sessions, Max Login Failures, Forbidden Login Time, SE Linux AVCs, SE Linux Enforcement Changes, Abnormal Program Termination, Promiscuous Socket Changes, and watched account logins. + +.SH OPTIONS +.TP +.B \-\-test +Take input from stdin and write prelude events to stdout but does not send them to the prelude\-manager. This can be used for debugging or testing the system with suspicious log files when you do not want it to alert or react. + +.SH INSTALLATION +This sensor has to be registered with the prelude\-manager before it will work properly. If the prelude\-manager is on the same host as the sensor, you will need to open two windows to register. If not, you will have to adjust this example to fit your environment. + +In one window, type: + +.B prelude\-admin register auditd "idmef:w" localhost \-\-uid 0 \-\-gid 0 + +In another, type: + +.B prelude\-admin registration\-server prelude\-manager + +Follow the on\-screen instructions to complete the registration. + +.SH TIPS +If you are aggregating multiple machines, you should enable node information in the audit event stream. You can do this in one of two places. If you want computer node names written to disk as well as sent in the realtime event stream, edit the name_format option in /etc/audit/auditd.conf. If you only want the node names in the realtime event stream, then edit the name_format option in /etc/audisp/audispd.conf. Do not enable both as it will put 2 node fields in the event stream. + +At this point, if you want have audit: forbidden login location, max concurrent sessions, max login failures, and forbidden login time anomalies being reported, you have to setup pam modules correctly. The pam modules are respectively: pam_access, pam_limits, pam_tally2, and pam_time. Please see the respective pam module man pages for any instructions. + +For performance reasons, some audit events will not produce syscall records which contain additional information about events unless there is at least one audit rule loaded. If you do not have any additional audit rules, edit \fI/etc/audit/audit.rules\fP and add something simple that won't impact performace like this: \fB\-w /etc/shadow \-p wa\fP. This rule will watch the shadow file for writes or changes to its attributes. The additional audit information provided by having at least one rule will allow the plugin to give a more complete view of the alert it is sending. + +If you are wanting to get alerts on watched syscalls, watched files, watched execution, or something becoming executable, you need to add some keys to your audit rules. For example, if you have the following audit watch in \fI/etc/audit/audit.rules\fP: + +.B \-w /etc/shadow \-p wa + +and you want idmef alerts on this, you need to add \fB\-k ids\-file\-med\fP or something appropriate to signal to the plugin that this message is for it. The format of the key has a fixed format of keywords separated by a dash. It follows the form of +.IB ids \- type \- severity . +The \fItype\fP can be either \fBsys\fP, \fBfile\fP, \fBexec\fP, or \fBmkexe\fP depending on whether you want the event to be considered a watched_syscall, watched_file, watched_exec, or watched_mk_exe respectively. The \fIseverity\fP can be either \fBinfo\fP, \fBlow\fP, \fBmed\fP, or \fBhi\fP depending on how urgent you would like it to be. + +.SH EXAMPLE RULES +To alert on any use of the personality syscall: +.br +.B \-a always,exit \-S personality \-k ids\-sys\-med + +To alert on a user failing to access the shadow file: +.br +.B \-a always,exit \-F path=/etc/shadow \-F perms=wa \-F success=0 \-k ids\-file\-med + +To alert on the execution of a program: +.br +.B \-w /bin/ping \-p x \-k ids\-exe\-info + +To alert on users making exe's in their home dir (takes 2 rules): +.br +.B \-a always,exit \-S fchmodat \-F dir=/home \-F a2&0111 \-F filetype=file \-k ids\-mkexe\-hi +.br +.B \-a always,exit \-S fchmod,chmod \-F dir=/home \-F a1&0111 \-F filetype=file \-k ids\-mkexe\-hi + +.SH FILES +/etc/audisp/plugins.d/au\-prelude.conf, /etc/audit/auditd.conf, /etc/audisp/audispd.conf, /etc/audisp/audisp\-prelude.conf +.SH "SEE ALSO" +.BR audispd (8), +.BR prelude\-manager (1), +.BR auditd.conf (8), +.BR audispd.conf (8), +.BR audisp\-prelude.conf (5). +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/audisp/plugins/prelude/audisp-prelude.c b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.c new file mode 100644 index 00000000..f3dc65a0 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.c @@ -0,0 +1,2250 @@ +/* audisp-prelude.c -- + * Copyright 2008-09,2011-12 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBCAP_NG +#include +#endif +#include "libaudit.h" +#include "auparse.h" +#include "prelude-config.h" + +#define CONFIG_FILE "/etc/audisp/audisp-prelude.conf" +#define ANALYZER_MODEL "auditd" +#define ANALYZER_CLASS "HIDS" +#define ANALYZER_MANUFACTURER "Red Hat, http://people.redhat.com/sgrubb/audit/" +#define PRELUDE_FAIL_CHECK if (ret < 0) goto err; + +typedef enum { AS_LOGIN, AS_MAX_LOGIN_FAIL, AS_MAX_LOGIN_SESS, AS_ABEND, + AS_PROM, AS_MAC_STAT, AS_LOGIN_LOCATION, AS_LOGIN_TIME, AS_MAC, + AS_AUTH, AS_WATCHED_LOGIN, AS_WATCHED_FILE, AS_WATCHED_EXEC, AS_MK_EXE, + AS_MMAP0, AS_WATCHED_SYSCALL, AS_TTY, AS_TOTAL } as_description_t; +const char *assessment_description[AS_TOTAL] = { + "A user has attempted to login", + "The maximum allowed login failures for this account has been reached. This could be an attempt to gain access to the account by someone other than the real account holder.", + "The maximum allowed concurrent logins for this account has been reached.", + "An application terminated abnormally. An attacker may be trying to exploit a weakness in the program.", + "A program has opened or closed a promiscuous socket. If this is not expected, it could be an attacker trying to sniff traffic.", + "A program has changed SE Linux policy enforcement. If this is not expected, it could be an attempt to subvert the system.", + "A user attempted to login from a location that is not allowed. This could be an attempt to gain access to the account by someone other than the real account holder.", + "A user attempted to login during a time that the user should not be logging into the system. This could be an attempt to gain access to the account by someone other than the real account holder.", + "A program has tried to access something that is not allowed in the MAC policy. This could indicate an attacker trying to exploit a weakness in the program.", + "A user has attempted to use an authentication mechanism and failed. This could be an attempt to gain privileges that they are not supposed to have.", + "A user has logged in to an account that is being watched.", + "A user has attempted to access a file that is being watched.", + "A user has attempted to execute a program that is being watched.", + "A user has attempted to create an executable program", + "A program has attempted mmap a fixed memory page at an address sometimes used as part of a kernel exploit", + "A user has run a command that issued a watched syscall", + "A user has typed keystrokes on a terminal" +}; +typedef enum { M_NORMAL, M_TEST } output_t; +typedef enum { W_NO, W_FILE, W_EXEC, W_MK_EXE } watched_t; + +/* Global Data */ +static volatile int stop = 0; +static volatile int hup = 0; +static prelude_client_t *client = NULL; +static auparse_state_t *au = NULL; +static prelude_conf_t config; +static output_t mode = M_NORMAL; +static char *myhostname=NULL; + +/* Local declarations */ +static void handle_event(auparse_state_t *au, + auparse_cb_event_t cb_event_type, void *user_data); + +/* + * SIGTERM handler + */ +static void term_handler( int sig ) +{ + stop = 1; +} + +/* + * SIGHUP handler: re-read config + */ +static void hup_handler( int sig ) +{ + hup = 1; +} + +static void reload_config(void) +{ + hup = 0; +} + +static int setup_analyzer(idmef_analyzer_t *analyzer) +{ + int ret; + prelude_string_t *string; + + ret = idmef_analyzer_new_model(analyzer, &string); + PRELUDE_FAIL_CHECK; + prelude_string_set_dup(string, ANALYZER_MODEL); + + ret = idmef_analyzer_new_class(analyzer, &string); + PRELUDE_FAIL_CHECK; + prelude_string_set_dup(string, ANALYZER_CLASS); + + ret = idmef_analyzer_new_manufacturer(analyzer, &string); + PRELUDE_FAIL_CHECK; + prelude_string_set_dup(string, ANALYZER_MANUFACTURER); + + ret = idmef_analyzer_new_version(analyzer, &string); + PRELUDE_FAIL_CHECK; + prelude_string_set_dup(string, PACKAGE_VERSION); + + return 0; + + err: + syslog(LOG_ERR, "%s: IDMEF error: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + return -1; +} + +static int init_prelude(int argc, char *argv[]) +{ + int ret; + prelude_client_flags_t flags; + + ret = prelude_thread_init(NULL); + ret = prelude_init(&argc, argv); + if (ret < 0) { + syslog(LOG_ERR, + "Unable to initialize the Prelude library: %s.\n", + prelude_strerror(ret)); + return -1; + } + ret = prelude_client_new(&client, + config.profile ? config.profile : ANALYZER_MODEL); + if (! client) { + syslog(LOG_ERR, + "Unable to create a prelude client object: %s.\n", + prelude_strerror(ret)); + return -1; + } + ret = setup_analyzer(prelude_client_get_analyzer(client)); + if (ret < 0) { + syslog(LOG_ERR, "Unable to setup analyzer: %s\n", + prelude_strerror(ret)); + + prelude_client_destroy(client, + PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + prelude_deinit(); + return -1; + } + if (mode == M_NORMAL) { + flags = prelude_client_get_flags(client); + flags |= PRELUDE_CLIENT_FLAGS_ASYNC_TIMER; + } else + flags = 0; // Debug mode + ret = prelude_client_set_flags(client, flags); + if (ret < 0) { + syslog(LOG_ERR, "Unable to set prelude client flags: %s\n", + prelude_strerror(ret)); + + prelude_client_destroy(client, + PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + prelude_deinit(); + return -1; + } + ret = prelude_client_start(client); + if (ret < 0) { + syslog(LOG_ERR, "Unable to start prelude client: %s\n", + prelude_strerror(ret)); + + prelude_client_destroy(client, + PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + prelude_deinit(); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + char tmp[MAX_AUDIT_MESSAGE_LENGTH+1]; + struct sigaction sa; + + if (argc > 1) { + if (argc == 2 && strcmp(argv[1], "--test") == 0) { + mode = M_TEST; + } else { + fprintf(stderr, "Usage: audisp-prelude [--test]\n"); + return 1; + } + } + + /* Register sighandlers */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + /* Set handler for the ones we care about */ + sa.sa_handler = term_handler; + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = hup_handler; + sigaction(SIGHUP, &sa, NULL); + if (load_config(&config, CONFIG_FILE)) { + if (mode == M_TEST) + puts("audisp-prelude is exiting on config load error"); + return 6; + } + + /* Initialize the auparse library */ + au = auparse_init(AUSOURCE_FEED, 0); + if (au == NULL) { + syslog(LOG_ERR, + "audisp-prelude is exiting due to auparse init errors"); + free_config(&config); + return -1; + } + auparse_add_callback(au, handle_event, NULL, NULL); + if (init_prelude(argc, argv)) { + if (mode == M_TEST) + puts("audisp-prelude is exiting due to init_prelude"); + else + syslog(LOG_ERR, + "audisp-prelude is exiting due to init_prelude failure"); + free_config(&config); + auparse_destroy(au); + return -1; + } +#ifdef HAVE_LIBCAP_NG + // Drop all capabilities + capng_clear(CAPNG_SELECT_BOTH); + capng_apply(CAPNG_SELECT_BOTH); +#endif + if (mode != M_TEST) + syslog(LOG_INFO, "audisp-prelude is ready for events"); + do { + fd_set read_mask; + struct timeval tv; + int retval; + + /* Load configuration */ + if (hup) { + reload_config(); + } + do { + tv.tv_sec = 5; + tv.tv_usec = 0; + FD_ZERO(&read_mask); + FD_SET(0, &read_mask); + if (auparse_feed_has_data(au)) + retval= select(1, &read_mask, NULL, NULL, &tv); + else + retval= select(1, &read_mask, NULL, NULL, NULL); } while (retval == -1 && errno == EINTR && !hup && !stop); + + /* Now the event loop */ + if (!stop && !hup && retval > 0) { + if (fgets_unlocked(tmp, MAX_AUDIT_MESSAGE_LENGTH, + stdin)){ + auparse_feed(au, tmp, strnlen(tmp, + MAX_AUDIT_MESSAGE_LENGTH)); + } + } else if (retval == 0) + auparse_flush_feed(au); + if (feof(stdin)) + break; + } while (stop == 0); + + /* Flush any accumulated events from queue */ + auparse_flush_feed(au); + + if (stop) { + if (mode == M_TEST) + puts("audisp-prelude is exiting on stop request"); + else + syslog(LOG_INFO, + "audisp-prelude is exiting on stop request"); + } else { + if (mode == M_TEST) + puts("audisp-prelude is exiting due to end of file"); + else + syslog(LOG_INFO, + "audisp-prelude is exiting due to losing input source"); + } + + /* Cleanup subsystems */ + if (client) + prelude_client_destroy(client, + PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); + prelude_deinit(); + auparse_destroy(au); + free_config(&config); + free(myhostname); + + return 0; +} + +static void print_test_message(idmef_message_t *idmef) +{ + int ret; + prelude_io_t *fd; + + ret = prelude_io_new(&fd); + if ( ret < 0 ) + return; + + prelude_io_set_file_io(fd, stdout); + idmef_message_print(idmef, fd); + + prelude_io_destroy(fd); +} + +static void send_idmef(prelude_client_t *client, idmef_message_t *idmef) +{ + if (mode == M_TEST) + print_test_message(idmef); + else + prelude_client_send_idmef(client, idmef); +} + +static int new_alert_common(auparse_state_t *au, idmef_message_t **idmef, + idmef_alert_t **alert) +{ + int ret; + idmef_time_t *dtime, *ctime; + time_t au_time; + + ret = idmef_message_new(idmef); + PRELUDE_FAIL_CHECK; + + ret = idmef_message_new_alert(*idmef, alert); + PRELUDE_FAIL_CHECK; + + idmef_alert_set_analyzer(*alert, + idmef_analyzer_ref(prelude_client_get_analyzer(client)), + IDMEF_LIST_PREPEND); + + // Put the audit time and message ID in the event + au_time = auparse_get_time(au); + ret = idmef_time_new_from_time(&dtime, &au_time); + PRELUDE_FAIL_CHECK; + idmef_alert_set_detect_time(*alert, dtime); + + // Set time this was created + ret = idmef_time_new_from_gettimeofday(&ctime); + PRELUDE_FAIL_CHECK; + idmef_alert_set_create_time(*alert, ctime); + + return 0; + err: + syslog(LOG_ERR, "%s: IDMEF error: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + idmef_message_destroy(*idmef); + return -1; +} + +static int get_loginuid(auparse_state_t *au) +{ + int uid; + const char *auid; + + auparse_first_field(au); + auid = auparse_find_field(au, "auid"); + if (auid) + uid = auparse_get_field_int(au); + else + uid = -1; + return uid; +} + +static int get_new_gid(auparse_state_t *au) +{ + int gid; + const char *ngid; + + auparse_first_field(au); + ngid = auparse_find_field(au, "new_gid"); + if (ngid) + gid = auparse_get_field_int(au); + else + gid = -1; + return gid; +} + +/* + * This function seeks to the specified record returning its type on succees + */ +static int goto_record_type(auparse_state_t *au, int type) +{ + int cur_type; + + auparse_first_record(au); + do { + cur_type = auparse_get_type(au); + if (cur_type == type) { + auparse_first_field(au); + return type; // Normal exit + } + } while (auparse_next_record(au) > 0); + + return -1; +} + +static int get_loginuid_info(auparse_state_t *au, idmef_user_id_t *user_id) +{ + int ret, type, is_num = 0; + const char *auid; + + type = auparse_get_type(au); + auparse_first_field(au); + auid = auparse_find_field(au, "acct"); + if (auid == NULL) { + is_num = 1; + goto_record_type(au, type); + auid = auparse_find_field(au, "sauid"); + if (auid == NULL) { + goto_record_type(au, type); + if (type == AUDIT_USER_LOGIN) { + // login programs write auid at second uid + auparse_find_field(au, "uid"); + auparse_next_field(au); + auid = auparse_find_field(au, "uid"); + } else { + auid = auparse_find_field(au, "auid"); + } + } + } + if (auid) { + prelude_string_t *str; + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + if (is_num) { + int uid = auparse_get_field_int(au); + idmef_user_id_set_number(user_id, uid); + } else { + struct passwd *pw; + pw = getpwnam(auid); + if (pw) + idmef_user_id_set_number(user_id, pw->pw_uid); + } + + auid = auparse_interpret_field(au); + ret = prelude_string_set_ref(str, auid); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_name(user_id, str); + } + return 0; + + err: + return -1; +} + +static int get_tty_info(auparse_state_t *au, idmef_user_id_t *user_id) +{ + int ret, type; + const char *tty; + + type = auparse_get_type(au); + auparse_first_field(au); + tty = auparse_find_field(au, "terminal"); + if (tty == NULL) { + goto_record_type(au, type); + tty = auparse_find_field(au, "tty"); + } + if (tty) { + prelude_string_t *str; + + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + ret = prelude_string_set_ref(str, tty); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_tty(user_id, str); + } + return 0; + err: + return -1; +} + +static int is_ipv4(const char *addr) +{ + int i = 0; + while (addr[i]) { + if ((addr[i] != '.') && !isdigit(addr[i])) + return 0; + i++; + } + return 1; +} + +static int is_ipv6(const char *addr) +{ + int i = 0; + while (addr[i]) { + if ((addr[i] != '.') && addr[i] != ':' && !isdigit(addr[i])) + return 0; + i++; + } + return 1; +} + +static int fill_in_node(idmef_node_t *node, const char *addr) +{ + int ret; + prelude_string_t *str; + + /* Setup the address string */ + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_ref(str, addr); + PRELUDE_FAIL_CHECK; + + /* Now figure out the kind of address */ + if (is_ipv4(addr)) { + idmef_address_t *my_addr; + ret = idmef_address_new(&my_addr); + PRELUDE_FAIL_CHECK; + idmef_address_set_category(my_addr, + IDMEF_ADDRESS_CATEGORY_IPV4_ADDR); + idmef_address_set_address(my_addr, str); + idmef_node_set_address(node, my_addr, 0); + } else if (is_ipv6(addr)){ + idmef_address_t *my_addr; + ret = idmef_address_new(&my_addr); + PRELUDE_FAIL_CHECK; + idmef_address_set_category(my_addr, + IDMEF_ADDRESS_CATEGORY_IPV6_ADDR); + idmef_address_set_address(my_addr, str); + idmef_node_set_address(node, my_addr, 0); + } else { /* Just a host name */ + idmef_node_set_name(node, str); + } + + return 0; + err: + return -1; +} + +static int get_rhost_info(auparse_state_t *au, idmef_source_t *source) +{ + int ret; + idmef_node_t *node; + const char *hostname; + + auparse_first_field(au); + hostname = auparse_find_field(au, "hostname"); + if (hostname) { + if (strcmp(hostname, "?") == 0) { + auparse_next_field(au); + hostname = auparse_get_field_str(au); + } + } else { /* Some AVCs have the remote addr */ + auparse_first_field(au); + hostname = auparse_find_field(au, "laddr"); + } + + if (hostname) { + ret = idmef_source_new_node(source, &node); + PRELUDE_FAIL_CHECK; + idmef_node_set_category(node, IDMEF_NODE_CATEGORY_UNKNOWN); + + ret = fill_in_node(node, hostname); + PRELUDE_FAIL_CHECK; + } + + return 0; + err: + return -1; +} + +static int do_node_common(auparse_state_t *au, idmef_node_t *node) +{ + int ret; + const char *name; + + auparse_first_field(au); + name = auparse_find_field(au, "node"); + if (name == NULL) { + if (myhostname == NULL) { + char tmp_name[255]; + if (gethostname(tmp_name, sizeof(tmp_name)) == 0) + myhostname = strdup(tmp_name); + } + name = myhostname; + idmef_node_set_category(node, IDMEF_NODE_CATEGORY_HOSTS); + } else + idmef_node_set_category(node, IDMEF_NODE_CATEGORY_UNKNOWN); + + if (name) { + ret = fill_in_node(node, name); + PRELUDE_FAIL_CHECK; + } else + goto err; + + return 0; + err: + return -1; +} + +static int get_node_info(auparse_state_t *au, idmef_source_t *source, + idmef_target_t *target) +{ + int ret; + idmef_node_t *node; + + if (source) { + ret = idmef_source_new_node(source, &node); + PRELUDE_FAIL_CHECK; + + ret = do_node_common(au, node); + PRELUDE_FAIL_CHECK; + } + + if (target) { + ret = idmef_target_new_node(target, &node); + PRELUDE_FAIL_CHECK; + + ret = do_node_common(au, node); + PRELUDE_FAIL_CHECK; + } + + return 0; + err: + return -1; +} + +static int get_login_exe_info(auparse_state_t *au, idmef_target_t *target) +{ + int ret, type; + idmef_process_t *process; + const char *exe, *pid; + + ret = idmef_target_new_process(target, &process); + PRELUDE_FAIL_CHECK; + + type = auparse_get_type(au); + auparse_first_field(au); + pid = auparse_find_field(au, "pid"); + if (pid) + idmef_process_set_pid(process, auparse_get_field_int(au)); + + goto_record_type(au, type); + exe = auparse_find_field(au, "exe"); + if (exe) { + char *base; + prelude_string_t *str, *name_str; + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + exe = auparse_interpret_field(au); + ret = prelude_string_set_ref(str, exe); + PRELUDE_FAIL_CHECK; + idmef_process_set_path(process, str); + + /* Set process name, login events do not have comm fields */ + base = basename(exe); + ret = prelude_string_new(&name_str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_dup(name_str, base); + PRELUDE_FAIL_CHECK; + idmef_process_set_name(process, name_str); + } + + return 0; + err: + return -1; +} + +static int get_target_group_info(auparse_state_t *au, idmef_user_t *tuser) +{ + int ret; + const char *ngid; + + auparse_first_field(au); + ngid = auparse_find_field(au, "new_gid"); + if (ngid) { + int gid; + idmef_user_id_t *user_id; + prelude_string_t *str; + + ret = idmef_user_new_user_id(tuser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_GROUP_PRIVS); + + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + gid = auparse_get_field_int(au); + if (gid >= 0) + idmef_user_id_set_number(user_id, gid); + + ngid = auparse_interpret_field(au); + ret = prelude_string_set_ref(str, ngid); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_name(user_id, str); + } + + return 0; + err: + return -1; +} + +static int get_comm_info(auparse_state_t *au, idmef_source_t *source, + idmef_target_t *target) +{ + int ret, type, need_comm = 1; + idmef_process_t *process; + const char *exe, *pid; + + if (source) + ret = idmef_source_new_process(source, &process); + else if (target) + ret = idmef_target_new_process(target, &process); + else + return -1; + PRELUDE_FAIL_CHECK; + + type = auparse_get_type(au); + auparse_first_field(au); + pid = auparse_find_field(au, "pid"); + if (pid) + idmef_process_set_pid(process, auparse_get_field_int(au)); + + goto_record_type(au, type); + auparse_first_field(au); + exe = auparse_find_field(au, "comm"); + if (exe) { + prelude_string_t *str; + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + exe = auparse_interpret_field(au); + ret = prelude_string_set_ref(str, exe); + PRELUDE_FAIL_CHECK; + idmef_process_set_name(process, str); + need_comm = 0; + } + + goto_record_type(au, type); + exe = auparse_find_field(au, "exe"); + if (exe) { + prelude_string_t *str; + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + exe = auparse_interpret_field(au); + ret = prelude_string_set_ref(str, exe); + PRELUDE_FAIL_CHECK; + idmef_process_set_path(process, str); + + /* Set the process name if not set already */ + if (need_comm) { + prelude_string_t *name_str; + + char *base = basename(exe); + ret = prelude_string_new(&name_str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_dup(name_str, base); + idmef_process_set_name(process, name_str); + } + } + + return 0; + err: + return -1; +} + +/* + * Fill in a file record for idmef. Note that we always get the + * full path name unless we have an AVC. + */ +static int get_file_info(auparse_state_t *au, idmef_target_t *target, int full) +{ + int ret; + idmef_file_t *file; + const char *name; + char path[PATH_MAX+1]; + + ret = idmef_target_new_file(target, &file, 0); + PRELUDE_FAIL_CHECK; + + *path = 0; + if (full) { + const char *cwd; + auparse_first_field(au); + cwd = auparse_find_field(au, "cwd"); + if (cwd) { + if ((cwd = auparse_interpret_field(au))) + strcat(path, cwd); + } + // Loop across all PATH records in the event + goto_record_type(au, AUDIT_PATH); + name = NULL; + do { // Make sure that we have an actual file record + if (auparse_find_field(au, "mode")) { + int m = auparse_get_field_int(au); + if (S_ISREG(m)) { + // Now back up and get file name + auparse_first_field(au); + name = auparse_find_field(au, "name"); + break; + } + } + } while (auparse_next_record(au) > 0 && + auparse_get_type(au) == AUDIT_PATH); + } else { + // SE Linux AVC + int type = auparse_get_type(au); + auparse_first_field(au); + name = auparse_find_field(au, "path"); + if (name == NULL) { + goto_record_type(au, type); + name = auparse_find_field(au, "name"); + } + } + if (name) + name = auparse_interpret_field(au); + if (name) { + if (name[0] == '/') + strcpy(path, name); + else + strcat(path, name); + } + if (path[0] != 0) { + prelude_string_t *str; + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + + ret = prelude_string_set_dup(str, path); + PRELUDE_FAIL_CHECK; + if (path[0] == '/') { + char *base; + prelude_string_t *name_str; + + idmef_file_set_path(file, str); + base = basename(path); + if (base[0] == 0) + base = "/"; + ret = prelude_string_new(&name_str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_dup(name_str, base); + PRELUDE_FAIL_CHECK; + idmef_file_set_name(file, name_str); + } else + idmef_file_set_name(file, str); + } + idmef_file_set_category(file, IDMEF_FILE_CATEGORY_CURRENT); + + return 0; + err: + return -1; +} + +static int add_additional_data(idmef_alert_t *alert, const char *title, + const char *text) +{ + int ret; + idmef_additional_data_t *data; + prelude_string_t *str; + + ret = idmef_alert_new_additional_data(alert, &data, IDMEF_LIST_APPEND); + PRELUDE_FAIL_CHECK; + + ret = idmef_additional_data_new_meaning(data, &str); + PRELUDE_FAIL_CHECK; + + prelude_string_set_dup(str, title); + idmef_additional_data_set_type(data, IDMEF_ADDITIONAL_DATA_TYPE_STRING); + idmef_additional_data_set_string_ref(data, text); + return 0; + err: + return -1; +} + +static int add_serial_number_data(auparse_state_t *au, idmef_alert_t *alert) +{ + int ret; + idmef_additional_data_t *data; + prelude_string_t *str; + unsigned long serial; + char eid[24]; + + serial = auparse_get_serial(au); + snprintf(eid, sizeof(eid), "%lu", serial); + + ret = idmef_alert_new_additional_data(alert, &data, IDMEF_LIST_APPEND); + PRELUDE_FAIL_CHECK; + + ret = idmef_additional_data_new_meaning(data, &str); + PRELUDE_FAIL_CHECK; + + prelude_string_set_dup(str, "Audit event serial #"); + idmef_additional_data_set_type(data, IDMEF_ADDITIONAL_DATA_TYPE_STRING); + idmef_additional_data_set_string_dup(data, eid); + return 0; + err: + return -1; +} + +static int add_exit_data(auparse_state_t *au, idmef_alert_t *alert) +{ + const char *e_ptr; + + if (goto_record_type(au, AUDIT_SYSCALL) == -1) + goto err; + e_ptr = auparse_find_field(au, "exit"); + if (e_ptr) { + int ret; + idmef_additional_data_t *data; + prelude_string_t *str; + char exit_code[80]; + + snprintf(exit_code, sizeof(exit_code), "%d (%s)", + auparse_get_field_int(au), + auparse_interpret_field(au)); + + ret = idmef_alert_new_additional_data(alert, + &data, IDMEF_LIST_APPEND); + PRELUDE_FAIL_CHECK; + + ret = idmef_additional_data_new_meaning(data, &str); + PRELUDE_FAIL_CHECK; + + prelude_string_set_dup(str, "Audit syscall exit code:"); + idmef_additional_data_set_type(data, + IDMEF_ADDITIONAL_DATA_TYPE_STRING); + idmef_additional_data_set_string_dup(data, exit_code); + } + return 0; + err: + return -1; +} + +static int add_execve_data(auparse_state_t *au, idmef_alert_t *alert) +{ + int ret, i, len = 0; + idmef_additional_data_t *data; + prelude_string_t *str; + const char *msgptr; + char msg[256], var[16]; + + if (goto_record_type(au, AUDIT_EXECVE) != AUDIT_EXECVE) + return 0; + + msg[0] = 0; + for (i=0; i<8; i++) { + snprintf(var, sizeof(var), "a%d", i); + msgptr = auparse_find_field(au, var); + if (msgptr) { + char *ptr; + int len2; + len2 = asprintf(&ptr, "%s=%s ", var, + auparse_interpret_field(au)); + if (len2 < 0) { + ptr = NULL; + } else if (len2 > 0 && (len2 + len + 1) < sizeof(msg)) { + strcat(msg, ptr); + len += len2; + } + free(ptr); + } else + break; + } + + ret = idmef_alert_new_additional_data(alert, &data, IDMEF_LIST_APPEND); + PRELUDE_FAIL_CHECK; + + ret = idmef_additional_data_new_meaning(data, &str); + PRELUDE_FAIL_CHECK; + + prelude_string_set_dup(str, "Execve args"); + idmef_additional_data_set_type(data, IDMEF_ADDITIONAL_DATA_TYPE_STRING); + idmef_additional_data_set_string_dup(data, msg); + return 0; + err: + return -1; +} + +static int set_classification(idmef_alert_t *alert, const char *text) +{ + int ret; + idmef_classification_t *classification; + prelude_string_t *str; + + ret = idmef_alert_new_classification(alert, &classification); + PRELUDE_FAIL_CHECK; + ret = prelude_string_new(&str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_ref(str, text); + PRELUDE_FAIL_CHECK; + idmef_classification_set_text(classification, str); + + return 0; + err: + return -1; +} + +static int do_assessment(idmef_alert_t *alert, auparse_state_t *au, + idmef_impact_severity_t severity, idmef_impact_type_t type, + const char *descr) +{ + int ret; + idmef_assessment_t *assessment; + idmef_impact_t *impact; + idmef_impact_completion_t completion = IDMEF_IMPACT_COMPLETION_ERROR; + const char *result; + + auparse_first_record(au); + result = auparse_find_field(au, "res"); + if (result == NULL) { + auparse_first_record(au); + result = auparse_find_field(au, "success"); + } + if (result) { + if (strcmp(result, "yes") == 0) + completion = IDMEF_IMPACT_COMPLETION_SUCCEEDED; + else if (strcmp(result, "success") == 0) + completion = IDMEF_IMPACT_COMPLETION_SUCCEEDED; + else + completion = IDMEF_IMPACT_COMPLETION_FAILED; + } + + // Adjust the rating on AVC's based on if they succeeded or not + if (goto_record_type(au, AUDIT_AVC) == AUDIT_AVC) { + if (completion == IDMEF_IMPACT_COMPLETION_FAILED) + severity = IDMEF_IMPACT_SEVERITY_LOW; + } else if (goto_record_type(au, AUDIT_USER_AVC) == AUDIT_USER_AVC) { + if (completion == IDMEF_IMPACT_COMPLETION_FAILED) + severity = IDMEF_IMPACT_SEVERITY_LOW; + } + // If this is a segfault, they failed + if (goto_record_type(au, AUDIT_ANOM_ABEND) == AUDIT_ANOM_ABEND) + completion = IDMEF_IMPACT_COMPLETION_FAILED; + + ret = idmef_alert_new_assessment(alert, &assessment); + PRELUDE_FAIL_CHECK; + ret = idmef_assessment_new_impact(assessment, &impact); + PRELUDE_FAIL_CHECK; + idmef_impact_set_severity(impact, severity); + idmef_impact_set_type(impact, type); + if (descr) { + prelude_string_t *str; + ret = idmef_impact_new_description(impact, &str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_ref(str, descr); + PRELUDE_FAIL_CHECK; + } + + // FIXME: I think this is wrong. sb a way to express indeterminate + if (completion != IDMEF_IMPACT_COMPLETION_ERROR) + idmef_impact_set_completion(impact, completion); + + return 0; + err: + return -1; +} + +/* + * This is for login related alerts + */ +static int login_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert, const char *msg, + idmef_impact_severity_t severity, as_description_t num) +{ + int ret; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser, *tuser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_UNKNOWN); + + ret = get_rhost_info(au, source); + PRELUDE_FAIL_CHECK; + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + ret = idmef_target_new_user(target, &tuser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(tuser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(tuser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_TARGET_USER); + + auparse_first_record(au); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_login_exe_info(au, target); + PRELUDE_FAIL_CHECK; + + ret = get_node_info(au, NULL, target); + PRELUDE_FAIL_CHECK; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, msg); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + if (get_loginuid(au) == 0) + impact = IDMEF_IMPACT_TYPE_ADMIN; + else + impact = IDMEF_IMPACT_TYPE_USER; + ret = do_assessment(alert, au, severity, impact, + assessment_description[num]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "login_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for SE Linux AVC related alerts + */ +static int avc_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert) +{ + int ret, type; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact_type = IDMEF_IMPACT_TYPE_OTHER; + const char *seperm = NULL; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + if ((type = goto_record_type(au, AUDIT_SYSCALL)) == AUDIT_SYSCALL || + (type = goto_record_type(au, AUDIT_USER_AVC)) == AUDIT_USER_AVC) { + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, + IDMEF_USER_ID_TYPE_ORIGINAL_USER); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + ret = get_rhost_info(au, source); + PRELUDE_FAIL_CHECK; + } else if ((type = goto_record_type(au, AUDIT_AVC)) == AUDIT_AVC) { + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + } + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + + auparse_first_record(au); + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + type = goto_record_type(au, AUDIT_CWD); + if (type == AUDIT_CWD) { + ret = get_file_info(au, target, 1); + PRELUDE_FAIL_CHECK; + impact_type = IDMEF_IMPACT_TYPE_FILE; + } else if ((type = goto_record_type(au, AUDIT_AVC)) == AUDIT_AVC) { + seperm = auparse_find_field(au, "seperm"); + if (auparse_find_field(au, "path")) { + ret = get_file_info(au, target, 0); + impact_type = IDMEF_IMPACT_TYPE_FILE; + } else { + goto_record_type(au, AUDIT_AVC); + if (auparse_find_field(au, "name")) { + ret = get_file_info(au, target, 0); + impact_type = IDMEF_IMPACT_TYPE_FILE; + } + } + } + + /* Add AVC info for reference */ + if ((goto_record_type(au, AUDIT_AVC) == AUDIT_AVC) || + (goto_record_type(au, AUDIT_USER_AVC) == AUDIT_USER_AVC)) { + ret = add_additional_data(alert, "AVC Text", + auparse_get_record_text(au)); + PRELUDE_FAIL_CHECK; + } + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Detect mmap 0 here */ + type = AS_MAC; + if (seperm && strcmp(seperm, "mmap_zero") == 0) { + const char *tclass = auparse_find_field(au, "tclass"); + if (tclass && strcmp(tclass, "memprotect")) + type = AS_MMAP0; + } + + /* Describe event */ + if (type == AS_MAC) { + ret = set_classification(alert, "MAC Violation"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, IDMEF_IMPACT_SEVERITY_MEDIUM, + impact_type, assessment_description[AS_MAC]); + } else { + ret = set_classification(alert, "MMAP Page 0"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, IDMEF_IMPACT_SEVERITY_HIGH, + impact_type, assessment_description[AS_MMAP0]); + } + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "avc_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for Application Abnormal Termination related alerts + */ +static int app_term_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert) +{ + int ret; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser, *tuser; + idmef_user_id_t *user_id; + const char *sig; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + ret = idmef_target_new_user(target, &tuser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(tuser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(tuser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + + auparse_first_record(au); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, NULL, target); + PRELUDE_FAIL_CHECK; + + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + auparse_first_record(au); + sig = auparse_find_field(au, "sig"); + if (sig) { + sig = auparse_interpret_field(au); + ret = add_additional_data(alert, "Signal", sig); + PRELUDE_FAIL_CHECK; + } + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, "App Abnormal Termination"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, IDMEF_IMPACT_SEVERITY_MEDIUM, + IDMEF_IMPACT_TYPE_OTHER, + assessment_description[AS_ABEND]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "term_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is to alert that something has opened a promiscuous socket + */ +static int promiscuous_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert) +{ + int ret, type, old_prom=-1, new_prom=-1; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser, *tuser; + idmef_user_id_t *user_id; + const char *dev; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + type = goto_record_type(au, AUDIT_SYSCALL); + if (type == AUDIT_SYSCALL) { + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, + IDMEF_USER_ID_TYPE_ORIGINAL_USER); + + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + } + dev = auparse_find_field(au, "dev"); + if (dev) { + ret = add_additional_data(alert, "Device", dev); + PRELUDE_FAIL_CHECK; + } + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + ret = idmef_target_new_user(target, &tuser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(tuser, IDMEF_USER_CATEGORY_OS_DEVICE); + + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + type = goto_record_type(au, AUDIT_ANOM_PROMISCUOUS); + if (type == AUDIT_ANOM_PROMISCUOUS) { + const char *old_val, *new_val; + + auparse_first_field(au); + new_val = auparse_find_field(au, "prom"); + if (new_val) + new_prom = auparse_get_field_int(au); + old_val = auparse_find_field(au, "old_prom"); + if (old_val) + old_prom = auparse_get_field_int(au); + } + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + if (new_prom == 256 && old_prom == 0) + ret = set_classification(alert, "Promiscuous Socket Opened"); + else if (new_prom == 0 && old_prom == 256) + ret = set_classification(alert, "Promiscuous Socket Closed"); + else + ret = set_classification(alert, "Promiscuous Socket Changed"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, IDMEF_IMPACT_SEVERITY_INFO, + IDMEF_IMPACT_TYPE_RECON, + assessment_description[AS_PROM]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "promiscuous_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is to alert that something has changed the selinux enforcement + */ +static int mac_status_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert) +{ + int ret, type, old_enforce=-1, new_enforce=-1; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser, *tuser; + idmef_user_id_t *user_id; + idmef_impact_severity_t severity; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + + type = goto_record_type(au, AUDIT_SYSCALL); + if (type == AUDIT_SYSCALL) { + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + } + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + ret = idmef_target_new_user(target, &tuser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(tuser, IDMEF_USER_CATEGORY_OS_DEVICE); + + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + type = goto_record_type(au, AUDIT_MAC_STATUS); + if (type == AUDIT_MAC_STATUS) { + const char *old_val, *new_val; + + auparse_first_field(au); + new_val = auparse_find_field(au, "enforcing"); + if (new_val) + new_enforce = auparse_get_field_int(au); + old_val = auparse_find_field(au, "old_enforcing"); + if (old_val) + old_enforce = auparse_get_field_int(au); + } + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + if (new_enforce == 1 && old_enforce == 0) { + ret = set_classification(alert, "SE Linux Enforcement Enabled"); + severity = IDMEF_IMPACT_SEVERITY_LOW; + } else if (new_enforce == 0 && old_enforce == 1) { + ret = set_classification(alert,"SE Linux Enforcement Disabled"); + severity = IDMEF_IMPACT_SEVERITY_HIGH; + } else { + ret = set_classification(alert, "SE Linux Enforcement Changed"); + severity = IDMEF_IMPACT_SEVERITY_LOW; + } + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, severity, IDMEF_IMPACT_TYPE_OTHER, + assessment_description[AS_MAC_STAT]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "mac_status_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for authentication failure alerts + */ +static int auth_failure_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert, const char *msg, + idmef_impact_severity_t severity, as_description_t num) +{ + int ret, gid; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser, *tuser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + ret = idmef_target_new_user(target, &tuser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(tuser, IDMEF_USER_CATEGORY_APPLICATION); + + ret = get_target_group_info(au, tuser); + PRELUDE_FAIL_CHECK; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, msg); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + gid = get_new_gid(au); + if (gid == 0 || gid == 10) { // Root or wheel + impact = IDMEF_IMPACT_TYPE_ADMIN; + severity = IDMEF_IMPACT_SEVERITY_MEDIUM; + } else + impact = IDMEF_IMPACT_TYPE_USER; + ret = do_assessment(alert, au, severity, impact, + assessment_description[num]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "auth_failure_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for watched syscall related alerts + */ +static int watched_syscall_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert, idmef_impact_severity_t severity) +{ + int ret, rtype; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + /* We should only analyze the syscall */ + rtype = goto_record_type(au, AUDIT_SYSCALL); + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + + auparse_first_record(au); + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + rtype = goto_record_type(au, AUDIT_CWD); + if (rtype == AUDIT_CWD) { + ret = get_file_info(au, target, 1); + PRELUDE_FAIL_CHECK; + } + impact = IDMEF_IMPACT_TYPE_OTHER; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + ret = add_exit_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, "Watched Syscall"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, severity, impact, + assessment_description[AS_WATCHED_SYSCALL]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "watches_syscall_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for watched file related alerts + */ +static int watched_file_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert, idmef_impact_severity_t severity) +{ + int ret, rtype; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + + auparse_first_record(au); + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + rtype = goto_record_type(au, AUDIT_CWD); + if (rtype == AUDIT_CWD) { + ret = get_file_info(au, target, 1); + PRELUDE_FAIL_CHECK; + } + impact = IDMEF_IMPACT_TYPE_FILE; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, "Watched File"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + ret = do_assessment(alert, au, severity, impact, + assessment_description[AS_WATCHED_FILE]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "watches_file_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for watched executable related alerts + */ +static int watched_exec_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert, idmef_impact_severity_t severity) +{ + int ret, rtype; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + + auparse_first_record(au); + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + rtype = goto_record_type(au, AUDIT_CWD); + if (rtype == AUDIT_CWD) { + ret = get_file_info(au, target, 1); + PRELUDE_FAIL_CHECK; + } + + ret = add_execve_data(au, alert); + PRELUDE_FAIL_CHECK; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, "Watched Executable"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + if (get_loginuid(au) == 0) + impact = IDMEF_IMPACT_TYPE_ADMIN; + else + impact = IDMEF_IMPACT_TYPE_USER; + ret = do_assessment(alert, au, severity, impact, + assessment_description[AS_WATCHED_EXEC]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "watched_exec_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +/* + * This is for watching exe's being made related alerts + */ +static int watched_mk_exe_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert, idmef_impact_severity_t severity) +{ + int ret, rtype; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *suser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + /* Fill in information about the target of the event */ + ret = idmef_alert_new_target(alert, &target, -1); + PRELUDE_FAIL_CHECK; + + auparse_first_record(au); + ret = get_node_info(au, source, target); + PRELUDE_FAIL_CHECK; + + rtype = goto_record_type(au, AUDIT_CWD); + if (rtype == AUDIT_CWD) { + ret = get_file_info(au, target, 1); + PRELUDE_FAIL_CHECK; + } + impact = IDMEF_IMPACT_TYPE_FILE; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, "Executable Created"); + PRELUDE_FAIL_CHECK; + + ret = do_assessment(alert, au, severity, impact, + assessment_description[AS_MK_EXE]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "watched_mk_exe_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} + +static int account_is_watched(auparse_state_t *au) +{ + const char *auid; + + auparse_first_field(au); + auid = auparse_find_field(au, "auid"); + if (auid) { // This is for successful logins + int uid = auparse_get_field_int(au); + if (ilist_find_num(&config.watched_accounts, uid)) + return 1; + } else { // Now try failed logins to see if we know who they are + auparse_first_field(au); + if ((auid = auparse_find_field(au, "acct"))) { + struct passwd *pw = getpwnam(auid); + if (pw && ilist_find_num( + &config.watched_accounts, pw->pw_uid)) + return 1; + } + } + return 0; +} + +static idmef_impact_type_t lookup_itype(const char *kind) +{ + if (strcasecmp(kind, "sys") == 0) + return IDMEF_IMPACT_TYPE_OTHER; + if (strcasecmp(kind, "file") == 0) + return IDMEF_IMPACT_TYPE_FILE; + if (strcasecmp(kind, "exec") == 0) + return IDMEF_IMPACT_TYPE_USER; + if (strcasecmp(kind, "mkexe") == 0) + return IDMEF_IMPACT_TYPE_OTHER; + return IDMEF_IMPACT_TYPE_ERROR; +} + +static idmef_impact_severity_t lookup_iseverity(const char *severity) +{ + if (strncmp(severity, "inf", 3) == 0) + return IDMEF_IMPACT_SEVERITY_INFO; + if (strncmp(severity, "low", 3) == 0) + return IDMEF_IMPACT_SEVERITY_LOW; + if (strncmp(severity, "med", 3) == 0) + return IDMEF_IMPACT_SEVERITY_MEDIUM; + if (strncmp(severity, "hi", 2) == 0) + return IDMEF_IMPACT_SEVERITY_HIGH; + return IDMEF_IMPACT_SEVERITY_ERROR; +} + +static void handle_watched_syscalls(auparse_state_t *au, + idmef_message_t **idmef, idmef_alert_t **alert) +{ + if (config.watched_syscall == E_YES || config.watched_file == E_YES || + config.watched_exec == E_YES || + config.watched_mk_exe == E_YES) { + const char *keyptr; + char *ptr, *kindptr, *ratingptr; + char key[AUDIT_MAX_KEY_LEN+1]; + idmef_impact_type_t type; + idmef_impact_severity_t severity; + + /* If no key or key is not for the ids, return */ + auparse_first_field(au); + keyptr = auparse_find_field(au, "key"); + if (keyptr) + keyptr = auparse_interpret_field(au); + while (keyptr) { + if (strncmp(keyptr, "ids-", 4) == 0) + break; + keyptr = auparse_find_field_next(au); + if (keyptr) + keyptr = auparse_interpret_field(au); + } + if (keyptr == NULL) + return; + + /* This key is for us, parse it up */ + strncpy(key, keyptr, AUDIT_MAX_KEY_LEN); + key[AUDIT_MAX_KEY_LEN] = 0; + + ptr = strchr(key, '-'); // There has to be a - because strncmp + kindptr = ptr + 1; + ptr = strchr(kindptr, '-'); + if (ptr) { + *ptr = 0; + ratingptr = ptr +1; + } else // The rules are misconfigured + return; + + type = lookup_itype(kindptr); + severity = lookup_iseverity(ratingptr); + + if (type == IDMEF_IMPACT_TYPE_OTHER && + strcasecmp(kindptr, "sys") == 0 && + config.watched_syscall == E_YES && + config.watched_syscall_act == A_IDMEF) { + if (new_alert_common(au, idmef, alert) >= 0) + watched_syscall_alert(au, *idmef, *alert, + severity); + } else if (type == IDMEF_IMPACT_TYPE_FILE && + config.watched_file == E_YES && + config.watched_file_act == A_IDMEF) { + if (new_alert_common(au, idmef, alert) >= 0) + watched_file_alert(au, *idmef, *alert, + severity); + } else if (type == IDMEF_IMPACT_TYPE_USER && + config.watched_exec == E_YES && + config.watched_exec_act == A_IDMEF) { + if (new_alert_common(au, idmef, alert) >= 0) + watched_exec_alert(au, *idmef, *alert, + severity); + } else if (type == IDMEF_IMPACT_TYPE_OTHER && + strcasecmp(kindptr, "mkexe") == 0 && + config.watched_mk_exe == E_YES && + config.watched_mk_exe_act == A_IDMEF) { + if (new_alert_common(au, idmef, alert) >= 0) + watched_mk_exe_alert(au, *idmef, *alert, + severity); + } + } +} + +static int tty_alert(auparse_state_t *au, idmef_message_t *idmef, + idmef_alert_t *alert) +{ + int ret; + + idmef_source_t *source; + idmef_user_t *suser; + idmef_user_id_t *user_id; + idmef_impact_type_t impact_type; + idmef_assessment_t *assessment; + idmef_impact_t *impact; + idmef_impact_severity_t severity; + prelude_string_t *str; + idmef_impact_completion_t completion = IDMEF_IMPACT_COMPLETION_ERROR; + + /* Fill in information about the event's source */ + ret = idmef_alert_new_source(alert, &source, -1); + PRELUDE_FAIL_CHECK; + + ret = idmef_source_new_user(source, &suser); + PRELUDE_FAIL_CHECK; + idmef_user_set_category(suser, IDMEF_USER_CATEGORY_APPLICATION); + ret = idmef_user_new_user_id(suser, &user_id, 0); + PRELUDE_FAIL_CHECK; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + ret = get_loginuid_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_tty_info(au, user_id); + PRELUDE_FAIL_CHECK; + + ret = get_comm_info(au, source, NULL); + PRELUDE_FAIL_CHECK; + + ret = add_execve_data(au, alert); + PRELUDE_FAIL_CHECK; + + ret = add_serial_number_data(au, alert); + PRELUDE_FAIL_CHECK; + + /* Describe event */ + ret = set_classification(alert, "Keylogger"); + PRELUDE_FAIL_CHECK; + + /* Assess impact */ + if (get_loginuid(au) == 0) + impact_type = IDMEF_IMPACT_TYPE_ADMIN; + else + impact_type = IDMEF_IMPACT_TYPE_USER; + completion = IDMEF_IMPACT_COMPLETION_SUCCEEDED; + severity = IDMEF_IMPACT_SEVERITY_LOW; + + ret = idmef_alert_new_assessment(alert, &assessment); + PRELUDE_FAIL_CHECK; + ret = idmef_assessment_new_impact(assessment, &impact); + PRELUDE_FAIL_CHECK; + idmef_impact_set_severity(impact, severity); + PRELUDE_FAIL_CHECK; + idmef_impact_set_type(impact, impact_type); + PRELUDE_FAIL_CHECK; + ret = idmef_impact_new_description(impact, &str); + PRELUDE_FAIL_CHECK; + ret = prelude_string_set_ref(str, assessment_description[AS_TTY]); + PRELUDE_FAIL_CHECK; + + send_idmef(client, idmef); + idmef_message_destroy(idmef); + + return 0; + + err: + syslog(LOG_ERR, "tty_alert: IDMEF error: %s.\n", + prelude_strerror(ret)); + idmef_message_destroy(idmef); + return -1; +} +static void handle_event(auparse_state_t *au, + auparse_cb_event_t cb_event_type, void *user_data) +{ + int type, num=0; + idmef_message_t *idmef; + idmef_alert_t *alert; + + if (cb_event_type != AUPARSE_CB_EVENT_READY) + return; + + // Loop through the records in the event looking for one to process. + // We use physical record number because we may search around and + // move the cursor accidentally skipping a record. + while (auparse_goto_record_num(au, num) > 0) { + type = auparse_get_type(au); + switch (type) { + case AUDIT_AVC: +// case AUDIT_USER_AVC: ignore USER_AVC for now + if (config.avcs == E_NO) + break; + if (config.avcs_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0) + avc_alert(au, idmef, alert); + break; + case AUDIT_USER_LOGIN: + // Do normal login alert + if (config.logins == E_YES && + config.logins_act == A_IDMEF) { + if (new_alert_common(au, &idmef, &alert) >= 0){ + login_alert(au, idmef, alert, "Login", + IDMEF_IMPACT_SEVERITY_INFO, AS_LOGIN); + }} + // Next do watched account alerts + if (config.watched_acct == E_NO) + break; + if (config.watched_acct_act != A_IDMEF) + break; + else if (account_is_watched(au)) { + if (new_alert_common(au, &idmef, &alert) >= 0){ + login_alert(au, idmef, alert, + "Watched Account Login", + IDMEF_IMPACT_SEVERITY_MEDIUM, + AS_WATCHED_LOGIN); + }} + break; + case AUDIT_ANOM_LOGIN_FAILURES: + if (config.login_failure_max == E_NO) + break; + if (config.login_failure_max_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0){ + login_alert(au, idmef, alert, + "Max Failed Logins", + IDMEF_IMPACT_SEVERITY_LOW, + AS_MAX_LOGIN_FAIL); + } + break; + case AUDIT_ANOM_LOGIN_SESSIONS: + if (config.login_session_max == E_NO) + break; + if (config.login_session_max_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0){ + login_alert(au, idmef, alert, + "Max Concurrent Sessions", + IDMEF_IMPACT_SEVERITY_INFO, + AS_MAX_LOGIN_SESS); + } + break; + case AUDIT_ANOM_LOGIN_LOCATION: + if (config.login_location == E_NO) + break; + if (config.login_location_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0){ + login_alert(au, idmef, alert, + "Login From Forbidden Location", + IDMEF_IMPACT_SEVERITY_MEDIUM, + AS_LOGIN_LOCATION); + } + break; + case AUDIT_ANOM_LOGIN_TIME: + if (config.login_time == E_NO) + break; + if (config.login_time_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0){ + login_alert(au, idmef, alert, + "Login During Forbidden Time", + IDMEF_IMPACT_SEVERITY_LOW, + AS_LOGIN_TIME); + } + break; + case AUDIT_ANOM_ABEND: + if (config.abends == E_NO) + break; + if (config.abends_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0) + app_term_alert(au, idmef, alert); + break; + case AUDIT_ANOM_PROMISCUOUS: + if (config.promiscuous == E_NO) + break; + if (config.promiscuous_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0) + promiscuous_alert(au, idmef, alert); + break; + case AUDIT_MAC_STATUS: + if (config.mac_status == E_NO) + break; + if (config.mac_status_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0) + mac_status_alert(au, idmef, alert); + break; + case AUDIT_GRP_AUTH: + if (config.group_auth == E_NO) + break; + if (config.group_auth_act != A_IDMEF) + break; + else { + const char *result; + + // We only care about failures + auparse_first_field(au); + result = auparse_find_field(au, "res"); + if (result && strcmp(result, "failed")) + break; + if (new_alert_common(au, &idmef, &alert) >= 0){ + auth_failure_alert(au, idmef, alert, + "Group Authentication Failure", + IDMEF_IMPACT_SEVERITY_LOW, + AS_AUTH); + }} + break; + case AUDIT_SYSCALL: + handle_watched_syscalls(au, &idmef, &alert); + // The previous call moves the current record + auparse_goto_record_num(au, num); + break; + case AUDIT_TTY: + if (config.tty == E_NO) + break; + if (config.tty_act != A_IDMEF) + break; + if (new_alert_common(au, &idmef, &alert) >= 0) + tty_alert(au, idmef, alert); + break; + default: + break; + } + num++; + } +} + diff --git a/framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf new file mode 100644 index 00000000..ae499a86 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf @@ -0,0 +1,61 @@ +# +# This file controls the configuration of the audit based +# intrusion detection system, audisp-prelude. +# + +profile = auditd + +detect_avc = yes +avc_action = idmef + +detect_logins = yes +login_action = idmef +#login_acct_exceptions = + +detect_login_fail_max = yes +login_fail_max_action = idmef +#login_fail_max_acct_exceptions = + +detect_login_session_max = yes +login_session_max_action = idmef +#login_session_max_acct_exceptions = + +detect_login_location = yes +login_location_action = idmef +#login_location_acct_exceptions = + +detect_login_time = yes +login_time_action = idmef +#login_time_acct_exceptions = + +detect_abend = yes +abend_action = idmef + +detect_promiscuous = yes +promiscuous_action = idmef + +detect_mac_status = yes +mac_status_action = idmef + +detect_group_auth = yes +group_auth_action = idmef + +detect_watched_acct = yes +watched_acct_action = idmef +watched_accounts = 1-499 + +detect_watched_syscall = yes +watched_syscall_action = idmef + +detect_watched_file = yes +watched_file_action = idmef + +detect_watched_exec = yes +watched_exec_action = idmef + +detect_watched_mk_exe = yes +watched_mk_exe_action = idmef + +detect_tty = no +tty_action = idmef + diff --git a/framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf.5 b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf.5 new file mode 100644 index 00000000..b7228ed3 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/audisp-prelude.conf.5 @@ -0,0 +1,153 @@ +.TH AUDISP-PRELUDE.CONF: "5" "Mar 2008" "Red Hat" "System Administration Utilities" +.SH NAME +audisp-prelude.conf \- the audisp-prelude configuration file +.SH DESCRIPTION +\fBaudisp-prelude.conf\fP is the file that controls the configuration of the audit based intrusion detection system. There are 2 general kinds of configuration option types, enablers and actions. The enablers simply have +.IR yes "/" no " +as the only valid choices. + +The action options currently allow +.IR ignore ", and "idmef " +as its choices. The +.IR ignore +option means that the IDS still detects events, but only logs the detection in response. The +.IR idmef +option means that the IDS will send an IDMEF alert to the prelude manager upon detection. + +The configuration options that are available are as follows: + +.TP +.I profile +This is a one word character string that is used to identify the profile name in the prelude reporting tools. The default is auditd. +.TP +.I detect_avc +This an enabler that determines if the IDS should be examining SE Linux AVC events. The default is +.IR yes ". +.TP +.I avc_action +This is an action that determines what response should be taken whenever a SE Linux AVC is detected. The default is +.IR idmef ". +.TP +.I detect_login +This is an enabler that determines if the IDS should be examining login events. The default is +.IR yes ". +.TP +.I login_action +This is an action that determines what response should be taken whenever a login event is detected. The default is +.IR idmef ". +.TP +.I detect_login_fail_max +This is an enabler that determines if the IDS should be looking for maximum number of failed logins for an account. The default is +.IR yes ". +.TP +.I login_fail_max_action +This is an action that determines what response should be taken whenever the maximum number of failed logins for an account is detected. The default is +.IR idmef ". +.TP +.I detect_login_session_max +This is an enabler that determines if the IDS should be looking for maximum concurrent sessions limit for an account. The default is +.IR yes ". +.TP +.I login_session_max_action +This is an action that determines what response should be taken whenever the maximum concurrent sessions limit for an account is detected. The default is +.IR idmef ". +.TP +.I detect_login_location +This is an enabler that determines if the IDS should be looking for logins being attempted from a forbidden location. The default is +.IR yes ". +.TP +.I login_location_action +This is an action that determines what response should be taken whenever logins are attempted from a forbidden location. The default is +.IR idmef ". +.TP +.I detect_login_time_alerts +This is an enabler that determines if the IDS should be looking for logins attempted during a forbidden time. The default is +.IR yes ". +.TP +.I login_time_action +This is an action that determines what response should be taken whenever logins are attempted during a forbidden time. The default is +.IR idmef ". +.TP +.I detect_abend +This is an enabler that determines if the IDS should be looking for programs terminating for an abnormal reason. The default is +.IR yes ". +.TP +.I abend_action +This is an action that determines what response should be taken whenever programs terminate for an abnormal reason. The default is +.IR idmef ". +.TP +.I detect_promiscuous +This is an enabler that determines if the IDS should be looking for promiscuous sockets being opened. The default is +.IR yes ". +.TP +.I promiscuous_action +This is an action that determines what response should be taken whenever promiscuous sockets are detected open. The default is +.IR idmef ". +.TP +.I detect_mac_status +This is an enabler that determines if the IDS should be detecting changes made to the SE Linux MAC enforcement. The default is +.IR yes ". +.TP +.I mac_status_action +This is an action that determines what response should be taken whenever changes are made to the SE Linux MAC enforcement. The default is +.IR idmef ". +.TP +.I detect_group_auth +This is an enabler that determines if the IDS should be detecting whenever a user fails in changing their default group. The default is +.IR yes ". +.TP +.I group_auth_act +This is an action that determines what response should be taken whenever a user fails in changing their default group. The default is +.IR idmef ". +.TP +.I detect_watched_acct +This is an enabler that determines if the IDS should be detecting a user attempting to login on an account that is being watched. The accounts to watch is set by the +.IR watched_accounts +option. The default is +.IR yes ". +.TP +.I watched_acct_act +This is an action that determines what response should be taken whenever a user attempts to login on an account that is being watched. The default is +.IR idmef ". +.TP +.I watched_accounts +This option is a whitespace and comma separated list of accounts to watch. The accounts may be numeric or alphanumeric. If you want to include a range of accounts, separate them with a dash but no spaces. For example, to watch logins from bin to lp, use "bin-lp". Only successful logins are recorded. +.TP +.I detect_watched_syscall +This is an enabler that determines if the IDS should be detecting whenever a user runs a command that issues a syscall that is being watched. The default is +.IR yes ". +.TP +.I watched_syscall_act +This is an action that determines what response should be taken whenever a user runs a command that issues a syscall that is being watched. The default is +.IR idmef ". +.TP +.I detect_watched_file +This is an enabler that determines if the IDS should be detecting whenever a user accesses a file that is being watched. The default is +.IR yes ". +.TP +.I watched_file_act +This is an action that determines what response should be taken whenever a user accesses a file that is being watched. The default is +.IR idmef ". +.TP +.I detect_watched_exec +This is an enabler that determines if the IDS should be detecting whenever a user executes a program that is being watched. The default is +.IR yes ". +.TP +.I watched_exec_act +This is an action that determines what response should be taken whenever a user executes a program that is being watched. The default is +.IR idmef ". +.TP +.I detect_watched_mk_exe +This is an enabler that determines if the IDS should be detecting whenever a user creates a file that is executable. The default is +.IR yes ". +.TP +.I watched_mk_exe_act +This is an action that determines what response should be taken whenever a user creates a file that is executable. The default is +.IR idmef ". +.SH "SEE ALSO" +.BR audispd (8), +.BR audisp-prelude (8), +.BR prelude-manager (1). +.SH AUTHOR +Steve Grubb + diff --git a/framework/src/audit/audisp/plugins/prelude/prelude-config.c b/framework/src/audit/audisp/plugins/prelude/prelude-config.c new file mode 100644 index 00000000..3a360483 --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/prelude-config.c @@ -0,0 +1,844 @@ +/* prelude-config.c -- + * Copyright 2008,2010-2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "prelude-config.h" + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; + const char *option; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(struct nv_pair *, int, prelude_conf_t *); + int max_options; +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *f, char *buf); +static int nv_split(char *buf, struct nv_pair *nv); +static const struct kw_pair *kw_lookup(const char *val); +static int profile_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int avc_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int avc_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_failure_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_failure_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_session_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_session_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_location_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_location_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_time_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int login_time_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int abends_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int abends_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int promiscuous_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int promiscuous_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int mac_status_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int mac_status_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int group_auth_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int group_auth_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_acct_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_acct_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_accounts_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_syscall_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_syscall_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_file_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_file_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_exec_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_exec_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_mk_exe_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int watched_mk_exe_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int tty_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int tty_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config); +static int sanity_check(prelude_conf_t *config, const char *file); + +static const struct kw_pair keywords[] = +{ + {"profile", profile_parser, 0 }, + {"detect_avc", avc_parser, 0 }, + {"avc_action", avc_act_parser, 0 }, + {"detect_logins", login_parser, 0 }, + {"login_action", login_act_parser, 0 }, + {"detect_login_fail_max", login_failure_parser, 0 }, + {"login_fail_max_action", login_failure_act_parser, 0 }, + {"detect_login_session_max", login_session_parser, 0 }, + {"login_session_max_action", login_session_act_parser, 0 }, + {"detect_login_location", login_location_parser, 0 }, + {"login_location_action", login_location_act_parser, 0 }, + {"detect_login_time", login_time_parser, 0 }, + {"login_time_action", login_time_act_parser, 0 }, + {"detect_abend", abends_parser, 0 }, + {"abend_action", abends_act_parser, 0 }, + {"detect_promiscuous", promiscuous_parser, 0 }, + {"promiscuous_action", promiscuous_act_parser, 0 }, + {"detect_mac_status", mac_status_parser, 0 }, + {"mac_status_action", mac_status_act_parser, 0 }, + {"detect_group_auth", group_auth_parser, 0 }, + {"group_auth_action", group_auth_act_parser, 0 }, + {"detect_watched_acct", watched_acct_parser, 0 }, + {"watched_acct_action", watched_acct_act_parser, 0 }, + {"watched_accounts", watched_accounts_parser, 1 }, + {"detect_watched_syscall", watched_syscall_parser, 0 }, + {"watched_syscall_action", watched_syscall_act_parser, 0 }, + {"detect_watched_file", watched_file_parser, 0 }, + {"watched_file_action", watched_file_act_parser, 0 }, + {"detect_watched_exec", watched_exec_parser, 0 }, + {"watched_exec_action", watched_exec_act_parser, 0 }, + {"detect_watched_mk_exe", watched_mk_exe_parser, 0 }, + {"watched_mk_exe_action", watched_mk_exe_act_parser, 0 }, + {"detect_tty", tty_parser, 0 }, + {"tty_action", tty_act_parser, 0 }, + { NULL, NULL } +}; + +static const struct nv_list enabler_words[] = +{ + {"no", E_NO }, + {"yes", E_YES }, + { NULL, 0 } +}; + +static const struct nv_list action_words[] = +{ + {"ignore", A_IGNORE }, + {"idmef", A_IDMEF }, +// {"kill", A_KILL }, +// {"session", A_SESSION }, +// {"single", A_SINGLE }, +// {"halt", A_HALT }, + { NULL, 0 } +}; + +/* + * Set everything to its default value +*/ +void clear_config(prelude_conf_t *config) +{ + config->profile = strdup("auditd"); + config->avcs = E_YES; + config->avcs_act = A_IDMEF; + config->logins = E_YES; + config->logins_act = A_IDMEF; + config->login_failure_max = E_YES; + config->login_failure_max_act = A_IDMEF; + config->login_session_max = E_YES; + config->login_session_max_act = A_IDMEF; + config->login_location = E_YES; + config->login_location_act = A_IDMEF; + config->login_time = E_YES; + config->login_time_act = A_IDMEF; + config->abends = E_YES; + config->abends_act = A_IDMEF; + config->promiscuous = E_YES; + config->promiscuous_act = A_IDMEF; + config->mac_status = E_YES; + config->mac_status_act = A_IDMEF; + config->group_auth = E_YES; + config->group_auth_act = A_IDMEF; + config->watched_acct = E_YES; + config->watched_acct_act = A_IDMEF; + config->watched_syscall = E_YES; + config->watched_syscall_act = A_IDMEF; + config->watched_file = E_YES; + config->watched_file_act = A_IDMEF; + config->watched_exec = E_YES; + config->watched_exec_act = A_IDMEF; + config->watched_mk_exe = E_YES; + config->watched_mk_exe_act = A_IDMEF; + config->tty = E_NO; + config->tty_act = A_IDMEF; + ilist_create(&config->watched_accounts); +} + +int load_config(prelude_conf_t *config, const char *file) +{ + int fd, rc, mode, lineno = 1; + struct stat st; + FILE *f; + char buf[128]; + + clear_config(config); + + /* open the file */ + mode = O_RDONLY; + rc = open(file, mode); + if (rc < 0) { + free_config(config); + if (errno != ENOENT) { + syslog(LOG_ERR, "Error opening %s (%s)", file, + strerror(errno)); + return 1; + } + syslog(LOG_WARNING, + "Config file %s doesn't exist, skipping", file); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + if (fstat(fd, &st) < 0) { + free_config(config); + syslog(LOG_ERR, "Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + free_config(config); + syslog(LOG_ERR, "Error - %s isn't owned by root", + file); + close(fd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + free_config(config); + syslog(LOG_ERR, "Error - %s is world writable", + file); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + free_config(config); + syslog(LOG_ERR, "Error - %s is not a regular file", + file); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + free_config(config); + syslog(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf)) { + // convert line into name-value pair + const struct kw_pair *kw; + struct nv_pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + syslog(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, file); + break; + case 2: // no '=' sign + syslog(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, file); + break; + default: // something else went wrong... + syslog(LOG_ERR, + "Unknown error for line %d in %s", + lineno, file); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + free_config(config); + fclose(f); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + free_config(config); + syslog(LOG_ERR, + "Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, file); + fclose(f); + return 1; + } + + /* Check number of options */ + if (kw->max_options == 0 && nv.option != NULL) { + free_config(config); + syslog(LOG_ERR, + "Keyword \"%s\" has invalid option " + "\"%s\" in line %d of %s", + nv.name, nv.option, lineno, file); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(&nv, lineno, config); + if (rc != 0) { + free_config(config); + fclose(f); + return 1; // local parser puts message out + } + + lineno++; + } + + fclose(f); + if (lineno > 1) + return sanity_check(config, file); + return 0; +} + +static char *get_line(FILE *f, char *buf) +{ + if (fgets_unlocked(buf, 128, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) + *ptr = 0; + return buf; + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr, *saved; + + nv->name = NULL; + nv->value = NULL; + nv->option = NULL; + ptr = strtok_r(buf, " ", &saved); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int profile_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (nv->value) { + free((char*)config->profile); + config->profile = strdup(nv->value); + } + return 0; +} + +static int lookup_enabler(const char *value, enable_t *enabled) +{ + int i; + for (i=0; enabler_words[i].name != NULL; i++) { + if (strcasecmp(value, enabler_words[i].name) == 0) { + *enabled = enabler_words[i].option; + return 0; + } + } + return 1; +} + +static int lookup_action(const char *value, action_t *action) +{ + int i; + for (i=0; action_words[i].name != NULL; i++) { + if (strcasecmp(value, action_words[i].name) == 0) { + *action = action_words[i].option; + return 0; + } + } + return 1; +} + +static int avc_parser(struct nv_pair *nv, int line, prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->avcs) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int avc_act_parser(struct nv_pair *nv, int line, prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->avcs_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_parser(struct nv_pair *nv, int line, prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->logins) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->logins_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_failure_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->login_failure_max) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_failure_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->login_failure_max_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_session_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->login_session_max) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_session_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->login_session_max_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_location_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->login_location) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_location_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->login_location_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_time_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->login_time) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int login_time_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->login_time_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int abends_parser(struct nv_pair *nv, int line, prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->abends) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int abends_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->abends_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int promiscuous_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->promiscuous) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int promiscuous_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->promiscuous_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int mac_status_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->mac_status) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int mac_status_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->mac_status_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int group_auth_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->group_auth) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int group_auth_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->group_auth_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_acct_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->watched_acct) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_acct_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->watched_acct_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int string_is_numeric(const char *s) +{ + if (*s == 0) + return 0; + do { + if (!isdigit(*s)) + return 0; + s++; + } while (*s); + return 1; +} + +static int watched_accounts_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + char *str = (char *)nv->value; + do { + char *ptr = strchr(str, '-'); + if (ptr) { + char *user1, *user2; + int start, end, i; + + user1 = str; + *ptr = 0; + user2 = ptr+1; + if (string_is_numeric(user1)) { + start = strtoul(user1, NULL, 10); + } else { + struct passwd *pw; + pw = getpwnam(user1); + if (pw == NULL) { + syslog(LOG_ERR, + "user %s is invalid - line %d, skipping", + user1, line); + continue; + } + start = pw->pw_uid; + } + i = strlen(user2); + if (i>0 && user2[i-1] == ',') + user2[i-1] = 0; + if (string_is_numeric(user2)) { + end = strtoul(user2, NULL, 10); + } else { + struct passwd *pw; + pw = getpwnam(user2); + if (pw == NULL) { + syslog(LOG_ERR, + "user %s is invalid - line %d, skipping", + user2, line); + continue; + } + end = pw->pw_uid; + } + if (start >= end) { + syslog(LOG_ERR, + "%s is larger or equal to %s, please fix, skipping", + user1, user2); + continue; + } + for (i=start; i<=end; i++) { + ilist_add_if_uniq( + &config->watched_accounts, i); + } + } else { + int acct; + if (string_is_numeric(str)) + acct = strtoul(str, NULL, 10); + else { + struct passwd *pw; + pw = getpwnam(str); + if (pw == NULL) { + syslog(LOG_ERR, + "user %s is invalid - line %d, skipping", + str, line); + continue; + } + acct = pw->pw_uid; + } + ilist_add_if_uniq(&config->watched_accounts, acct); + } + str = strtok(NULL, ", "); + } while(str); + + return 0; +} + +static int watched_syscall_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->watched_syscall) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_syscall_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->watched_syscall_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_file_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->watched_file) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_file_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->watched_file_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_exec_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->watched_exec) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_exec_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->watched_exec_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_mk_exe_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->watched_mk_exe) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int watched_mk_exe_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->watched_mk_exe_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int tty_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_enabler(nv->value, &config->tty) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int tty_act_parser(struct nv_pair *nv, int line, + prelude_conf_t *config) +{ + if (lookup_action(nv->value, &config->tty_act) == 0) + return 0; + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} +/* + * This function is where we do the integrated check of the audispd config + * options. At this point, all fields have been read. Returns 0 if no + * problems and 1 if problems detected. + */ +static int sanity_check(prelude_conf_t *config, const char *file) +{ + /* Error checking */ + return 0; +} + +void free_config(prelude_conf_t *config) +{ + free((void *)config->profile); + ilist_clear(&config->watched_accounts); +} + diff --git a/framework/src/audit/audisp/plugins/prelude/prelude-config.h b/framework/src/audit/audisp/plugins/prelude/prelude-config.h new file mode 100644 index 00000000..f9d1c14a --- /dev/null +++ b/framework/src/audit/audisp/plugins/prelude/prelude-config.h @@ -0,0 +1,76 @@ +/* prelude-config.h -- + * Copyright 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef PRELUDE_CONFIG_H +#define PRELUDE_CONFIG_H + +#include "audisp-int.h" + +typedef enum { E_NO, E_YES } enable_t; +typedef enum { A_IGNORE, A_IDMEF=1, A_KILL=2, A_SESSION=4, A_SINGLE=8, + A_HALT=16 } action_t; + +typedef struct prelude_conf +{ + const char *profile; + enable_t avcs; + action_t avcs_act; + enable_t logins; + action_t logins_act; + enable_t login_failure_max; + action_t login_failure_max_act; + enable_t login_session_max; + action_t login_session_max_act; + enable_t login_location; + action_t login_location_act; + enable_t login_time; + action_t login_time_act; + enable_t abends; + action_t abends_act; + enable_t promiscuous; + action_t promiscuous_act; + enable_t mac_status; + action_t mac_status_act; + enable_t group_auth; + action_t group_auth_act; + enable_t watched_acct; + action_t watched_acct_act; + ilist watched_accounts; + enable_t watched_syscall; + action_t watched_syscall_act; + enable_t watched_file; + action_t watched_file_act; + enable_t watched_exec; + action_t watched_exec_act; + enable_t watched_mk_exe; + action_t watched_mk_exe_act; + enable_t tty; + action_t tty_act; +} prelude_conf_t; + +void clear_config(prelude_conf_t *config); +int load_config(prelude_conf_t *config, const char *file); +void free_config(prelude_conf_t *config); + +#endif + diff --git a/framework/src/audit/audisp/plugins/remote/Makefile.am b/framework/src/audit/audisp/plugins/remote/Makefile.am new file mode 100644 index 00000000..8440e908 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/Makefile.am @@ -0,0 +1,51 @@ +# Makefile.am -- +# Copyright 2008-2009,2011,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +EXTRA_DIST = au-remote.conf audisp-remote.conf notes.txt $(man_MANS) +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib +prog_confdir = $(sysconfdir)/audisp +prog_conf = audisp-remote.conf +plugin_confdir=$(prog_confdir)/plugins.d +plugin_conf = au-remote.conf +sbin_PROGRAMS = audisp-remote +noinst_HEADERS = remote-config.h queue.h remote-fgets.h +man_MANS = audisp-remote.8 audisp-remote.conf.5 +check_PROGRAMS = test-queue +TESTS = $(check_PROGRAMS) + +audisp_remote_SOURCES = audisp-remote.c remote-config.c queue.c remote-fgets.c +audisp_remote_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE -Wundef +audisp_remote_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now $(gss_libs) +audisp_remote_LDADD = $(CAPNG_LDADD) + +test_queue_SOURCES = queue.c test-queue.c + +install-data-hook: + mkdir -p -m 0750 ${DESTDIR}${plugin_confdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/$(plugin_conf) ${DESTDIR}${plugin_confdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/$(prog_conf) ${DESTDIR}${prog_confdir} + +uninstall-hook: + rm ${DESTDIR}${plugin_confdir}/$(plugin_conf) + rm ${DESTDIR}${prog_confdir}/$(prog_conf) + diff --git a/framework/src/audit/audisp/plugins/remote/au-remote.conf b/framework/src/audit/audisp/plugins/remote/au-remote.conf new file mode 100644 index 00000000..e0adf96c --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/au-remote.conf @@ -0,0 +1,12 @@ + +# This file controls the audispd data path to the +# remote event logger. This plugin will send events to +# a remote machine (Central Logger). + +active = no +direction = out +path = /sbin/audisp-remote +type = always +#args = +format = string + diff --git a/framework/src/audit/audisp/plugins/remote/audisp-remote.8 b/framework/src/audit/audisp/plugins/remote/audisp-remote.8 new file mode 100644 index 00000000..6f3b5fe6 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/audisp-remote.8 @@ -0,0 +1,34 @@ +.TH AUDISP-REMOTE: "8" "Apr 2011" "Red Hat" "System Administration Utilities" +.SH NAME +audisp-remote \- plugin for remote logging +.SH SYNOPSIS +.B audisp-remote +.SH DESCRIPTION +\fBaudisp-remote\fP is a plugin for the audit event dispatcher daemon, audispd, that preforms remote logging to an aggregate logging server. + +.SH TIPS +If you are aggregating multiple machines, you should enable node information in the audit event stream. You can do this in one of two places. If you want computer node names written to disk as well as sent in the realtime event stream, edit the name_format option in /etc/audit/auditd.conf. If you only want the node names in the realtime event stream, then edit the name_format option in /etc/audisp/audispd.conf. Do not enable both as it will put 2 node fields in the event stream. + +.SH SIGNALS +.TP +SIGUSR1 +Causes the audisp-remote program to write the value of some of its internal flags to syslog. The +.IR suspend +flag tells whether or not logging has been suspended. The +.IR transport_ok +flag tells whether or not the connection to the remote server is healthy. The +.IR queue_size +tells how many records are enqueued to be sent to the remote server. +.TP +SIGUSR2 +Causes the audisp-remote program to resume logging if it were suspended due to an error. + +.SH FILES +/etc/audisp/plugins.d/au-remote.conf, /etc/audit/auditd.conf, /etc/audisp/audispd.conf, /etc/audisp/audisp-remote.conf +.SH "SEE ALSO" +.BR audispd (8), +.BR auditd.conf(8), +.BR audispd.conf(8), +.BR audisp-remote.conf(5). +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/audisp/plugins/remote/audisp-remote.c b/framework/src/audit/audisp/plugins/remote/audisp-remote.c new file mode 100644 index 00000000..2585c78c --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/audisp-remote.c @@ -0,0 +1,1486 @@ +/* audisp-remote.c -- + * Copyright 2008-2012 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_GSSAPI +#include +#include +#include +#endif +#ifdef HAVE_LIBCAP_NG +#include +#endif +#include "libaudit.h" +#include "private.h" +#include "remote-config.h" +#include "queue.h" +#include "remote-fgets.h" + +#define CONFIG_FILE "/etc/audisp/audisp-remote.conf" +#define BUF_SIZE 32 + +/* MAX_AUDIT_MESSAGE_LENGTH, aligned to 4 KB so that an average q_append() only + writes to two disk disk blocks (1 aligned data block, 1 header block). */ +#define QUEUE_ENTRY_SIZE (3*4096) + +/* Error types */ +#define ET_SUCCESS 0 +#define ET_PERMANENT -1 +#define ET_TEMPORARY -2 + +/* Global Data */ +static volatile int stop = 0; +static volatile int hup = 0; +static volatile int suspend = 0; +static volatile int dump = 0; +static volatile int transport_ok = 0; +static volatile int sock=-1; +static volatile int remote_ended = 0, quiet = 0; +static int ifd; +remote_conf_t config; + +/* Constants */ +static const char *SINGLE = "1"; +static const char *HALT = "0"; +static const char *INIT_PGM = "/sbin/init"; +static const char *SPOOL_FILE = "/var/spool/audit/remote.log"; + +/* Local function declarations */ +static int check_message(void); +static int relay_event(const char *s, size_t len); +static int init_transport(void); +static int stop_transport(void); +static int ar_read (int, void *, int); +static int ar_write (int, const void *, int); + +#ifdef USE_GSSAPI +/* We only ever talk to one server, so we don't need per-connection + credentials. These are the ones we talk to the server with. */ +gss_ctx_id_t my_context; + +#define REQ_FLAGS GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG +#define USE_GSS (config.enable_krb5) +#endif + +/* Compile-time expression verification */ +#define verify(E) do { \ + char verify__[(E) ? 1 : -1]; \ + (void)verify__; \ + } while (0) + +/* + * SIGTERM handler + */ +static void term_handler( int sig ) +{ + stop = 1; +} + +/* + * SIGHUP handler: re-read config + */ +static void hup_handler( int sig ) +{ + hup = 1; +} + +static void reload_config(void) +{ + stop_transport(); // FIXME: We should only stop transport if necessary + hup = 0; +} + +/* + * SIGSUR1 handler: dump stats + */ +static void user1_handler( int sig ) +{ + dump = 1; +} + +static void dump_stats(struct queue *queue) +{ + syslog(LOG_INFO, "suspend=%s, transport_ok=%s, queue_size=%zu", + suspend ? "yes" : "no", + transport_ok ? "yes" : "no", + q_queue_length(queue)); + dump = 0; +} + +/* + * SIGSUR2 handler: resume logging + */ +static void user2_handler( int sig ) +{ + suspend = 0; +} + +/* + * SIGCHLD handler: reap exiting processes + */ +static void child_handler(int sig) +{ + while (waitpid(-1, NULL, WNOHANG) > 0) + ; /* empty */ +} + +/* + * Handlers for various events coming back from the remote server. + * Return -1 if the remote dispatcher should exit. + */ + +/* Loss of sync - got an invalid response. */ +static int sync_error_handler (const char *why) +{ + /* "why" has human-readable details on why we've lost (or will + be losing) sync. Sync errors are transient - if a retry + doesn't fix it, we eventually call network_failure_handler + which has all the user-tweakable actions. */ + syslog (LOG_ERR, "lost/losing sync, %s", why); + return 0; +} + +static void change_runlevel(const char *level) +{ + char *argv[3]; + int pid; + + pid = fork(); + if (pid < 0) { + syslog(LOG_ALERT, + "audisp-remote failed to fork switching runlevels"); + return; + } + if (pid) /* Parent */ + return; + + /* Child */ + argv[0] = (char *)INIT_PGM; + argv[1] = (char *)level; + argv[2] = NULL; + execve(INIT_PGM, argv, NULL); + syslog(LOG_ALERT, "audisp-remote failed to exec %s", INIT_PGM); + exit(1); +} + +static void safe_exec(const char *exe, const char *message) +{ + char *argv[3]; + int pid; + + if (exe == NULL) { + syslog(LOG_ALERT, + "Safe_exec passed NULL for program to execute"); + return; + } + + pid = fork(); + if (pid < 0) { + syslog(LOG_ALERT, + "audisp-remote failed to fork doing safe_exec"); + return; + } + if (pid) /* Parent */ + return; + + /* Child */ + argv[0] = (char *)exe; + argv[1] = (char *)message; + argv[2] = NULL; + execve(exe, argv, NULL); + syslog(LOG_ALERT, "audisp-remote failed to exec %s", exe); + exit(1); +} + +static int do_action (const char *desc, const char *message, + int log_level, + failure_action_t action, const char *exe) +{ + switch (action) + { + case FA_IGNORE: + return 0; + case FA_SYSLOG: + syslog (log_level, "%s, %s", desc, message); + return 0; + case FA_EXEC: + safe_exec (exe, message); + return 0; + case FA_SUSPEND: + syslog (log_level, + "suspending remote logging due to %s", desc); + suspend = 1; + return 0; + case FA_RECONNECT: + syslog (log_level, + "remote logging disconnected due to %s, will attempt reconnection", + desc); + return 0; + case FA_SINGLE: + syslog (log_level, + "remote logging is switching system to single user mode due to %s", + desc); + change_runlevel(SINGLE); + return -1; + case FA_HALT: + syslog (log_level, + "remote logging halting system due to %s", desc); + change_runlevel(HALT); + return -1; + case FA_STOP: + syslog (log_level, "remote logging stopping due to %s, %s", + desc, message); + stop = 1; + return -1; + } + syslog (log_level, "unhandled action %d for %s", action, desc); + return -1; +} + +static int network_failure_handler (const char *message) +{ + return do_action ("network failure", message, + LOG_WARNING, + config.network_failure_action, + config.network_failure_exe); +} + +static int remote_disk_low_handler (const char *message) +{ + return do_action ("remote server is low on disk space", message, + LOG_WARNING, + config.disk_low_action, config.disk_low_exe); +} + +static int remote_disk_full_handler (const char *message) +{ + return do_action ("remote server's disk is full", message, + LOG_ERR, + config.disk_full_action, config.disk_full_exe); +} + +static int remote_disk_error_handler (const char *message) +{ + return do_action ("remote server has a disk error", message, + LOG_ERR, + config.disk_error_action, config.disk_error_exe); +} + +static int remote_server_ending_handler (const char *message) +{ + stop_transport(); + remote_ended = 1; + return do_action ("remote server is going down", message, + LOG_NOTICE, + config.remote_ending_action, + config.remote_ending_exe); +} + +static int generic_remote_error_handler (const char *message) +{ + return do_action ("unrecognized remote error", message, + LOG_ERR, config.generic_error_action, + config.generic_error_exe); +} + +static int generic_remote_warning_handler (const char *message) +{ + return do_action ("unrecognized remote warning", message, + LOG_WARNING, + config.generic_warning_action, + config.generic_warning_exe); +} + +/* Report and handle a queue error, using errno. */ +static void queue_error(void) +{ + char *errno_str; + + errno_str = strerror(errno); + do_action("queue error", errno_str, LOG_ERR, config.queue_error_action, + config.queue_error_exe); +} + +static void send_heartbeat (void) +{ + relay_event (NULL, 0); +} + +static void do_overflow_action(void) +{ + switch (config.overflow_action) + { + case OA_IGNORE: + break; + case OA_SYSLOG: + syslog(LOG_ERR, "queue is full - dropping event"); + break; + case OA_SUSPEND: + syslog(LOG_ALERT, + "Audisp-remote is suspending event processing due to overflowing its queue."); + suspend = 1; + break; + case OA_SINGLE: + syslog(LOG_ALERT, + "Audisp-remote is now changing the system to single user mode due to overflowing its queue"); + change_runlevel(SINGLE); + break; + case OA_HALT: + syslog(LOG_ALERT, + "Audisp-remote is now halting the system due to overflowing its queue"); + change_runlevel(HALT); + break; + default: + syslog(LOG_ALERT, "Unknown overflow action requested"); + break; + } +} + +/* Initialize and return a queue depending on user's configuration. + On error return NULL and set errno. */ +static struct queue *init_queue(void) +{ + const char *path; + int q_flags; + + if (config.queue_file != NULL) + path = config.queue_file; + else + path = SPOOL_FILE; + q_flags = Q_IN_MEMORY; + if (config.mode == M_STORE_AND_FORWARD) + /* FIXME: let user control Q_SYNC? */ + q_flags |= Q_IN_FILE | Q_CREAT | Q_RESIZE; + verify(QUEUE_ENTRY_SIZE >= MAX_AUDIT_MESSAGE_LENGTH); + return q_open(q_flags, path, config.queue_depth, QUEUE_ENTRY_SIZE); +} + +/* Send a record from QUEUE to the remote system */ +static void send_one(struct queue *queue) +{ + char event[MAX_AUDIT_MESSAGE_LENGTH]; + int len; + + if (suspend || !transport_ok) + return; + + len = q_peek(queue, event, sizeof(event)); + if (len == 0) + return; + if (len < 0) { + queue_error(); + return; + } + + /* We send len -1 to remove trailing \n */ + if (relay_event(event, len-1) < 0) + return; + + if (q_drop_head(queue) != 0) + queue_error(); +} + +int main(int argc, char *argv[]) +{ + struct sigaction sa; + struct queue *queue; + int rc; + size_t q_len; + + /* Register sighandlers */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + /* Set handler for the ones we care about */ + sa.sa_handler = term_handler; + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = hup_handler; + sigaction(SIGHUP, &sa, NULL); + sa.sa_handler = user1_handler; + sigaction(SIGUSR1, &sa, NULL); + sa.sa_handler = user2_handler; + sigaction(SIGUSR2, &sa, NULL); + sa.sa_handler = child_handler; + sigaction(SIGCHLD, &sa, NULL); + if (load_config(&config, CONFIG_FILE)) + return 6; + + (void) umask( umask( 077 ) | 027 ); + // ifd = open("test.log", O_RDONLY); + ifd = 0; + fcntl(ifd, F_SETFL, O_NONBLOCK); + + /* We fail here if the transport can't be initialized because of some + * permanent (i.e. operator) problem, such as misspelled host name. */ + rc = init_transport(); + if (rc == ET_PERMANENT) + return 1; + queue = init_queue(); + if (queue == NULL) { + syslog(LOG_ERR, "Error initializing audit record queue: %m"); + return 1; + } + +#ifdef HAVE_LIBCAP_NG + // Drop capabilities + capng_clear(CAPNG_SELECT_BOTH); + if (config.local_port && config.local_port < 1024) + capng_update(CAPNG_ADD, CAPNG_EFFECTIVE|CAPNG_PERMITTED, + CAP_NET_BIND_SERVICE); + capng_apply(CAPNG_SELECT_BOTH); +#endif + syslog(LOG_NOTICE, "Audisp-remote started with queue_size: %zu", + q_queue_length(queue)); + + while (stop == 0) { //FIXME break out when socket is closed + fd_set rfd, wfd; + struct timeval tv; + char event[MAX_AUDIT_MESSAGE_LENGTH]; + int n, fds = ifd + 1; + + /* Load configuration */ + if (hup) + reload_config(); + + if (dump) + dump_stats(queue); + + /* Setup select flags */ + FD_ZERO(&rfd); + FD_SET(ifd, &rfd); // input fd + FD_ZERO(&wfd); + if (sock > 0) { + // Setup socket to read acks from server + FD_SET(sock, &rfd); // remote socket + if (sock > ifd) + fds = sock + 1; + // If we have anything in the queue, + // find out if we can send it + if (q_queue_length(queue) && !suspend && transport_ok) + FD_SET(sock, &wfd); + } + + if (config.heartbeat_timeout > 0) { + tv.tv_sec = config.heartbeat_timeout; + tv.tv_usec = 0; + n = select(fds, &rfd, &wfd, NULL, &tv); + } else + n = select(fds, &rfd, &wfd, NULL, NULL); + if (n < 0) + continue; // If here, we had some kind of problem + + if ((config.heartbeat_timeout > 0) && n == 0 && !remote_ended) { + /* We attempt a hearbeat if select fails, which + * may give us more heartbeats than we need. This + * is safer than too few heartbeats. */ + quiet = 1; + send_heartbeat(); + quiet = 0; + continue; + } + + // See if we got a shutdown message from the server + if (sock > 0 && FD_ISSET(sock, &rfd)) + check_message(); + + // If we broke out due to one of these, cycle to start + if (hup != 0 || stop != 0) + continue; + + // See if input fd is also set + if (FD_ISSET(ifd, &rfd)) { + do { + if (remote_fgets(event, sizeof(event), ifd)) { + if (!transport_ok && remote_ended && + config.remote_ending_action == + FA_RECONNECT) { + quiet = 1; + if (init_transport() == + ET_SUCCESS) + remote_ended = 0; + quiet = 0; + } + /* Strip out EOE records */ + if (*event == 't') { + if (strncmp(event, + "type=EOE", 8) == 0) + continue; + } else { + char *ptr = strchr(event, ' '); + if (ptr) { + ptr++; + if (strncmp(ptr, + "type=EOE", + 8) == 0) + continue; + } else + continue; //malformed + } + if (q_append(queue, event) != 0) { + if (errno == ENOSPC) + do_overflow_action(); + else + queue_error(); + } + } else if (remote_fgets_eof()) + stop = 1; + } while (remote_fgets_more(sizeof(event))); + } + // See if output fd is also set + if (sock > 0 && FD_ISSET(sock, &wfd)) { + // If so, try to drain backlog + while (q_queue_length(queue) && !suspend && + !stop && transport_ok) + send_one(queue); + } + } + if (sock >= 0) { + shutdown(sock, SHUT_RDWR); + close(sock); + } + free_config(&config); + q_len = q_queue_length(queue); + q_close(queue); + if (stop) + syslog(LOG_NOTICE, "audisp-remote is exiting on stop request, queue_size: %zu", q_len); + + return q_len ? 1 : 0; +} + +#ifdef USE_GSSAPI + +/* Communications under GSS is done by token exchanges. Each "token" may + contain a message, perhaps signed, perhaps encrypted. The messages within + are what we're interested in, but the network sees the tokens. The + protocol we use for transferring tokens is to send the length first, + four bytes MSB first, then the token data. We return nonzero on error. */ +static int recv_token(int s, gss_buffer_t tok) +{ + int ret; + unsigned char lenbuf[4]; + unsigned int len; + + ret = ar_read(s, (char *) lenbuf, 4); + if (ret < 0) { + syslog(LOG_ERR, "GSS-API error reading token length"); + return -1; + } else if (!ret) { + return 0; + } else if (ret != 4) { + syslog(LOG_ERR, "GSS-API error reading token length"); + return -1; + } + + len = ( ((uint32_t)(lenbuf[0] & 0xFF) << 24) + | ((uint32_t)(lenbuf[1] & 0xFF) << 16) + | ((uint32_t)(lenbuf[2] & 0xFF) << 8) + | (uint32_t)(lenbuf[3] & 0xFF)); + + if (len > MAX_AUDIT_MESSAGE_LENGTH) { + syslog(LOG_ERR, + "GSS-API error: event length excedes MAX_AUDIT_LENGTH"); + return -1; + } + tok->length = len; + tok->value = (char *) malloc(tok->length ? tok->length : 1); + if (tok->length && tok->value == NULL) { + syslog(LOG_ERR, "Out of memory allocating token data %zd %zx", + tok->length, tok->length); + return -1; + } + + ret = ar_read(s, (char *) tok->value, tok->length); + if (ret < 0) { + syslog(LOG_ERR, "GSS-API error reading token data"); + free(tok->value); + return -1; + } else if (ret != (int) tok->length) { + syslog(LOG_ERR, "GSS-API error reading token data"); + free(tok->value); + return -1; + } + + return 0; +} + +/* Same here. */ +int send_token(int s, gss_buffer_t tok) +{ + int ret; + unsigned char lenbuf[4]; + unsigned int len; + + if (tok->length > 0xffffffffUL) + return -1; + + len = tok->length; + lenbuf[0] = (len >> 24) & 0xff; + lenbuf[1] = (len >> 16) & 0xff; + lenbuf[2] = (len >> 8) & 0xff; + lenbuf[3] = len & 0xff; + + ret = ar_write(s, (char *) lenbuf, 4); + if (ret < 0) { + syslog(LOG_ERR, "GSS-API error sending token length"); + return -1; + } else if (ret != 4) { + syslog(LOG_ERR, "GSS-API error sending token length"); + return -1; + } + + ret = ar_write(s, tok->value, tok->length); + if (ret < 0) { + syslog(LOG_ERR, "GSS-API error sending token data"); + return -1; + } else if (ret != (int) tok->length) { + syslog(LOG_ERR, "GSS-API error sending token data"); + return -1; + } + + return 0; +} + +static void gss_failure_2 (const char *msg, int status, int type) +{ + OM_uint32 message_context = 0; + OM_uint32 min_status = 0; + gss_buffer_desc status_string; + + do { + gss_display_status (&min_status, + status, + type, + GSS_C_NO_OID, + &message_context, + &status_string); + + syslog (LOG_ERR, "GSS error: %s: %s", + msg, (char *)status_string.value); + + gss_release_buffer(&min_status, &status_string); + } while (message_context != 0); +} + +static void gss_failure (const char *msg, int major_status, int minor_status) +{ + gss_failure_2 (msg, major_status, GSS_C_GSS_CODE); + if (minor_status) + gss_failure_2 (msg, minor_status, GSS_C_MECH_CODE); +} + +#define KCHECK(x,f) if (x) { \ + syslog (LOG_ERR, "krb5 error: %s in %s\n", krb5_get_error_message (kcontext, x), f); \ + return -1; } + +#define KEYTAB_NAME "/etc/audisp/audisp-remote.key" +#define CCACHE_NAME "MEMORY:audisp-remote" + +/* Each time we connect to the server, we negotiate a set of credentials and + a security context. To do this, we need our own credentials first. For + other Kerberos applications, the user will have called kinit (or otherwise + authenticated) first, but we don't have that luxury. So, we implement part + of kinit here. When our tickets expire, the usual close/open/retry logic + has us calling here again, where we re-init and get new tickets. */ +static int negotiate_credentials (void) +{ + gss_buffer_desc empty_token_buf = { 0, (void *) "" }; + gss_buffer_t empty_token = &empty_token_buf; + gss_buffer_desc send_tok, recv_tok, *token_ptr; + gss_ctx_id_t *gss_context = &my_context; + gss_buffer_desc name_buf; + gss_name_t service_name_e; + OM_uint32 major_status, minor_status, init_sec_min_stat; + OM_uint32 ret_flags; + + /* Getting an initial ticket is outside the scope of GSS, so + we use Kerberos calls here. */ + + int krberr; + krb5_context kcontext = NULL; + char *realm_name; + krb5_principal audit_princ; + krb5_ccache ccache = NULL; + krb5_creds my_creds; + krb5_get_init_creds_opt options; + krb5_keytab keytab = NULL; + const char *krb5_client_name; + char *slashptr; + char host_name[255]; + struct stat st; + const char *key_file; + + token_ptr = GSS_C_NO_BUFFER; + *gss_context = GSS_C_NO_CONTEXT; + recv_tok.value = NULL; + + krberr = krb5_init_context (&kcontext); + KCHECK (krberr, "krb5_init_context"); + + if (config.krb5_key_file) + key_file = config.krb5_key_file; + else + key_file = KEYTAB_NAME; + unsetenv ("KRB5_KTNAME"); + setenv ("KRB5_KTNAME", key_file, 1); + + if (stat (key_file, &st) == 0) { + if ((st.st_mode & 07777) != 0400) { + if (!quiet) + syslog (LOG_ERR, + "%s is not mode 0400 (it's %#o) - compromised key?", + key_file, st.st_mode & 07777); + return -1; + } + if (st.st_uid != 0) { + if (!quiet) + syslog (LOG_ERR, + "%s is not owned by root (it's %d) - compromised key?", + key_file, st.st_uid); + return -1; + } + } + + /* This looks up the default real (*our* realm) from + /etc/krb5.conf (or wherever) */ + krberr = krb5_get_default_realm (kcontext, &realm_name); + KCHECK (krberr, "krb5_get_default_realm"); + + krb5_client_name = config.krb5_client_name ? + config.krb5_client_name : "auditd"; + if (gethostname(host_name, sizeof(host_name)) != 0) { + if (!quiet) + syslog (LOG_ERR, + "gethostname: host name longer than %ld characters?", + sizeof (host_name)); + return -1; + } + + syslog (LOG_ERR, "kerberos principal: %s/%s@%s\n", + krb5_client_name, host_name, realm_name); + /* Encode our own "name" as auditd/remote@EXAMPLE.COM. */ + krberr = krb5_build_principal (kcontext, &audit_princ, + strlen(realm_name), realm_name, + krb5_client_name, host_name, NULL); + KCHECK (krberr, "krb5_build_principal"); + + /* Locate our machine's key table, where our private key is + * held. */ + krberr = krb5_kt_resolve (kcontext, key_file, &keytab); + KCHECK (krberr, "krb5_kt_resolve"); + + /* Identify a cache to hold the key in. The GSS wrappers look + up our credentials here. */ + krberr = krb5_cc_resolve (kcontext, CCACHE_NAME, &ccache); + KCHECK (krberr, "krb5_cc_resolve"); + + setenv("KRB5CCNAME", CCACHE_NAME, 1); + + memset(&my_creds, 0, sizeof(my_creds)); + memset(&options, 0, sizeof(options)); + krb5_get_init_creds_opt_set_address_list(&options, NULL); + krb5_get_init_creds_opt_set_forwardable(&options, 0); + krb5_get_init_creds_opt_set_proxiable(&options, 0); + krb5_get_init_creds_opt_set_tkt_life(&options, 24*60*60); + + /* Load our credentials from the key table. */ + krberr = krb5_get_init_creds_keytab(kcontext, &my_creds, audit_princ, + keytab, 0, NULL, + &options); + KCHECK (krberr, "krb5_get_init_creds_keytab"); + + /* Create the cache... */ + krberr = krb5_cc_initialize(kcontext, ccache, audit_princ); + KCHECK (krberr, "krb5_cc_initialize"); + + /* ...and store our credentials in it. */ + krberr = krb5_cc_store_cred(kcontext, ccache, &my_creds); + KCHECK (krberr, "krb5_cc_store_cred"); + + /* The GSS code now has a set of credentials for this program. + I.e. we know who "we" are. Now we talk to the server to + get its credentials and set up a security context for encryption. */ + if (config.krb5_principal == NULL) { + const char *name = config.krb5_client_name ? + config.krb5_client_name : "auditd"; + config.krb5_principal = (char *) malloc (strlen (name) + 1 + + strlen (config.remote_server) + 1); + sprintf((char *)config.krb5_principal, "%s@%s", + name, config.remote_server); + } + slashptr = strchr (config.krb5_principal, '/'); + if (slashptr) + *slashptr = '@'; + + name_buf.value = (char *)config.krb5_principal; + name_buf.length = strlen(name_buf.value) + 1; + major_status = gss_import_name(&minor_status, &name_buf, + (gss_OID) gss_nt_service_name, &service_name_e); + if (major_status != GSS_S_COMPLETE) { + gss_failure("importing name", major_status, minor_status); + return -1; + } + + /* Someone has to go first. In this case, it's us. */ + if (send_token(sock, empty_token) < 0) { + (void) gss_release_name(&minor_status, &service_name_e); + return -1; + } + + /* The server starts this loop with the token we just sent + (the empty one). We start this loop with "no token". */ + token_ptr = GSS_C_NO_BUFFER; + *gss_context = GSS_C_NO_CONTEXT; + + do { + /* Give GSS a chance to digest what we have so far. */ + major_status = gss_init_sec_context(&init_sec_min_stat, + GSS_C_NO_CREDENTIAL, gss_context, + service_name_e, NULL, REQ_FLAGS, 0, + NULL, /* no channel bindings */ + token_ptr, NULL, /* ignore mech type */ + &send_tok, &ret_flags, NULL); /* ignore time_rec */ + + if (token_ptr != GSS_C_NO_BUFFER) + free(recv_tok.value); + + /* Send the server any tokens requested of us. */ + if (send_tok.length != 0) { + if (send_token(sock, &send_tok) < 0) { + (void) gss_release_buffer(&minor_status, + &send_tok); + (void) gss_release_name(&minor_status, + &service_name_e); + return -1; + } + } + (void) gss_release_buffer(&minor_status, &send_tok); + + if (major_status != GSS_S_COMPLETE + && major_status != GSS_S_CONTINUE_NEEDED) { + gss_failure("initializing context", major_status, + init_sec_min_stat); + (void) gss_release_name(&minor_status, &service_name_e); + if (*gss_context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor_status, + gss_context, GSS_C_NO_BUFFER); + return -1; + } + + /* Now get any tokens the sever sends back. We use + these back at the top of the loop. */ + if (major_status == GSS_S_CONTINUE_NEEDED) { + if (recv_token(sock, &recv_tok) < 0) { + (void) gss_release_name(&minor_status, + &service_name_e); + return -1; + } + token_ptr = &recv_tok; + } + } while (major_status == GSS_S_CONTINUE_NEEDED); + + (void) gss_release_name(&minor_status, &service_name_e); + +#if 0 + major_status = gss_inquire_context (&minor_status, &my_context, NULL, + &service_name_e, NULL, NULL, + NULL, NULL, NULL); + if (major_status != GSS_S_COMPLETE) { + gss_failure("inquiring target name", major_status, minor_status); + return -1; + } + major_status = gss_display_name(&minor_status, service_name_e, + &recv_tok, NULL); + gss_release_name(&minor_status, &service_name_e); + if (major_status != GSS_S_COMPLETE) { + gss_failure("displaying name", major_status, minor_status); + return -1; + } + syslog(LOG_INFO, "GSS-API Connected to: %s", + (char *)recv_tok.value); +#endif + return 0; +} +#endif + +static int stop_sock(void) +{ + if (sock >= 0) { + shutdown(sock, SHUT_RDWR); + close(sock); + } + sock = -1; + transport_ok = 0; + + return 0; +} + +static int stop_transport(void) +{ + int rc; + + switch (config.transport) + { + case T_TCP: + rc = stop_sock(); + break; + default: + rc = -1; + break; + } + return rc; +} + +static int init_sock(void) +{ + int rc; + struct addrinfo *ai; + struct addrinfo hints; + char remote[BUF_SIZE]; + int one=1; + + if (sock >= 0) { + syslog(LOG_NOTICE, "socket already setup"); + transport_ok = 1; + return ET_SUCCESS; + } + memset(&hints, '\0', sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV; + hints.ai_socktype = SOCK_STREAM; + snprintf(remote, BUF_SIZE, "%u", config.port); + rc = getaddrinfo(config.remote_server, remote, &hints, &ai); + if (rc) { + if (!quiet) + syslog(LOG_ERR, + "Error looking up remote host: %s - exiting", + gai_strerror(rc)); + if (rc == EAI_NONAME || rc == EAI_NODATA) + return ET_PERMANENT; + else + return ET_TEMPORARY; + } + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) { + if (!quiet) + syslog(LOG_ERR, "Error creating socket: %s", + strerror(errno)); + freeaddrinfo(ai); + return ET_TEMPORARY; + } + + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int)); + + if (config.local_port != 0) { + struct sockaddr_in address; + + memset (&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(config.local_port); + address.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(sock, (struct sockaddr *)&address, sizeof(address))) { + if (!quiet) + syslog(LOG_ERR, + "Cannot bind local socket to port %d", + config.local_port); + stop_sock(); + freeaddrinfo(ai); + return ET_TEMPORARY; + } + + } + if (connect(sock, ai->ai_addr, ai->ai_addrlen)) { + if (!quiet) + syslog(LOG_ERR, "Error connecting to %s: %s", + config.remote_server, strerror(errno)); + freeaddrinfo(ai); + stop_sock(); + return ET_TEMPORARY; + } + + freeaddrinfo(ai); + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof (int)); + + /* The idea here is to minimize the time between the message + and the ACK, assuming that individual messages are + infrequent enough that we can ignore the inefficiency of + sending the header and message in separate packets. */ + if (config.format == F_MANAGED) + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof (int)); + +#ifdef USE_GSSAPI + if (USE_GSS) { + if (negotiate_credentials ()) + return ET_PERMANENT; + } +#endif + + transport_ok = 1; + syslog(LOG_NOTICE, "Connected to %s", config.remote_server); + return ET_SUCCESS; +} + +static int init_transport(void) +{ + int rc; + + switch (config.transport) + { + case T_TCP: + rc = init_sock(); + // We set this so that it will retry the connection + if (rc == ET_TEMPORARY) + remote_ended = 1; + break; + default: + rc = ET_PERMANENT; + break; + } + return rc; +} + +static int ar_write (int sk, const void *buf, int len) +{ + int rc = 0, r; + while (len > 0) { + do { + r = write(sk, buf, len); + } while (r < 0 && errno == EINTR); + if (r < 0) { + if (errno == EPIPE) + stop_sock(); + return r; + } + if (r == 0) + break; + rc += r; + buf = (void *)((char *)buf + r); + len -= r; + } + return rc; +} + +static int ar_read (int sk, void *buf, int len) +{ + int rc = 0, r, timeout = config.max_time_per_record * 1000; + struct pollfd pfd; + + pfd.fd=sk; + pfd.events=POLLIN | POLLPRI | POLLHUP | POLLERR | POLLNVAL; + while (len > 0) { + do { + // reads can hang if cable is disconnected + int prc = poll(&pfd, (nfds_t) 1, timeout); + if (prc <= 0) + return -1; + r = read(sk, buf, len); + } while (r < 0 && errno == EINTR); + if (r < 0) { + if (errno == EPIPE) + stop_sock(); + return r; + } + if (r == 0) + break; + rc += r; + buf = (void *)((char *)buf + r); + len -= r; + } + return rc; +} + +static int relay_sock_ascii(const char *s, size_t len) +{ + int rc; + + if (len == 0) + return 0; + + if (!transport_ok) { + if (init_transport ()) + return -1; + } + + rc = ar_write(sock, s, len); + if (rc <= 0) { + stop = 1; + syslog(LOG_ERR,"Connection to %s closed unexpectedly - exiting", + config.remote_server); + return -1; + } + + return 0; +} + +#ifdef USE_GSSAPI + +/* Sending an encrypted message is pretty simple - wrap the message in + a token, and send the token. The server unwraps it to get the + original message. */ +static int send_msg_gss (unsigned char *header, const char *msg, uint32_t mlen) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc utok, etok; + int rc; + + utok.length = AUDIT_RMW_HEADER_SIZE + mlen; + utok.value = malloc (utok.length); + + memcpy (utok.value, header, AUDIT_RMW_HEADER_SIZE); + + if (msg != NULL && mlen > 0) + memcpy (utok.value+AUDIT_RMW_HEADER_SIZE, msg, mlen); + + major_status = gss_wrap (&minor_status, + my_context, + 1, + GSS_C_QOP_DEFAULT, + &utok, + NULL, + &etok); + if (major_status != GSS_S_COMPLETE) { + gss_failure("encrypting message", major_status, minor_status); + free (utok.value); + return -1; + } + rc = send_token (sock, &etok); + free (utok.value); + (void) gss_release_buffer(&minor_status, &etok); + + return rc ? -1 : 0; +} + +/* Likewise here. */ +static int recv_msg_gss (unsigned char *header, char *msg, uint32_t *mlen) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc utok, etok; + int hver, mver, rc; + uint32_t type, rlen, seq; + + rc = recv_token (sock, &etok); + if (rc) + return -1; + + major_status = gss_unwrap (&minor_status, my_context, &etok, + &utok, NULL, NULL); + if (major_status != GSS_S_COMPLETE) { + gss_failure("decrypting message", major_status, minor_status); + free (utok.value); + return -1; + } + + if (utok.length < AUDIT_RMW_HEADER_SIZE) { + sync_error_handler ("message too short"); + return -1; + } + memcpy (header, utok.value, AUDIT_RMW_HEADER_SIZE); + + if (! AUDIT_RMW_IS_MAGIC (header, AUDIT_RMW_HEADER_SIZE)) { + sync_error_handler ("bad magic number"); + return -1; + } + + AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq); + + if (rlen > MAX_AUDIT_MESSAGE_LENGTH) { + sync_error_handler ("message too long"); + return -1; + } + + memcpy (msg, utok.value+AUDIT_RMW_HEADER_SIZE, rlen); + + *mlen = rlen; + + return 0; +} +#endif + +static int send_msg_tcp (unsigned char *header, const char *msg, uint32_t mlen) +{ + int rc; + + rc = ar_write(sock, header, AUDIT_RMW_HEADER_SIZE); + if (rc <= 0) { + syslog(LOG_ERR, "send to %s failed", config.remote_server); + return 1; + } + + if (msg != NULL && mlen > 0) { + rc = ar_write(sock, msg, mlen); + if (rc <= 0) { + syslog(LOG_ERR, "send to %s failed", + config.remote_server); + return 1; + } + } + return 0; +} + +static int recv_msg_tcp (unsigned char *header, char *msg, uint32_t *mlen) +{ + int hver, mver, rc; + uint32_t type, rlen, seq; + + rc = ar_read (sock, header, AUDIT_RMW_HEADER_SIZE); + if (rc < 16) { + syslog(LOG_ERR, "read from %s failed", config.remote_server); + return -1; + } + + if (! AUDIT_RMW_IS_MAGIC (header, AUDIT_RMW_HEADER_SIZE)) { + /* FIXME: the right thing to do here is close the socket + * and start a new one. */ + sync_error_handler ("bad magic number"); + return -1; + } + + AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq); + + if (rlen > MAX_AUDIT_MESSAGE_LENGTH) { + sync_error_handler ("message too long"); + return -1; + } + + if (rlen > 0 && ar_read (sock, msg, rlen) < rlen) { + sync_error_handler ("ran out of data reading reply"); + return -1; + } + return 0; +} + +static int check_message_managed(void) +{ + unsigned char header[AUDIT_RMW_HEADER_SIZE]; + int hver, mver; + uint32_t type, rlen, seq; + char msg[MAX_AUDIT_MESSAGE_LENGTH+1]; + +#ifdef USE_GSSAPI + if (USE_GSS) { + if (recv_msg_gss (header, msg, &rlen)) { + stop_transport(); + return -1; + } + } else +#endif + if (recv_msg_tcp(header, msg, &rlen)) { + stop_transport(); + return -1; + } + + AUDIT_RMW_UNPACK_HEADER(header, hver, mver, type, rlen, seq); + msg[rlen] = 0; + + if (type == AUDIT_RMW_TYPE_ENDING) + return remote_server_ending_handler(msg); + if (type == AUDIT_RMW_TYPE_DISKLOW) + return remote_disk_low_handler(msg); + if (type == AUDIT_RMW_TYPE_DISKFULL) + return remote_disk_full_handler(msg); + if (type == AUDIT_RMW_TYPE_DISKERROR) + return remote_disk_error_handler(msg); + return -1; +} + +/* This is to check for async notification like server is shutting down */ +static int check_message(void) +{ + int rc; + + switch (config.format) + { + case F_MANAGED: + rc = check_message_managed(); + break; +/* case F_ASCII: + rc = check_message_ascii(); + break; */ + default: + rc = -1; + break; + } + + return rc; +} + +static int relay_sock_managed(const char *s, size_t len) +{ + static int sequence_id = 1; + unsigned char header[AUDIT_RMW_HEADER_SIZE]; + int hver, mver; + uint32_t type, rlen, seq; + char msg[MAX_AUDIT_MESSAGE_LENGTH+1]; + int n_tries_this_message = 0; + time_t now, then = 0; + + sequence_id ++; + +try_again: + time (&now); + if (then == 0) + then = now; + + /* We want the first retry to be quick, in case the network + failed for some fail-once reason. In this case, it goes + "failure - reconnect - send". Only if this quick retry + fails do we start pausing between retries to prevent + swamping the local computer and the network. */ + if (n_tries_this_message > 1) + sleep (config.network_retry_time); + + if (n_tries_this_message > config.max_tries_per_record) { + network_failure_handler ("max retries exhausted"); + return -1; + } + if ((now - then) > config.max_time_per_record) { + network_failure_handler ("max retry time exhausted"); + return -1; + } + + n_tries_this_message ++; + + if (!transport_ok) { + if (init_transport ()) + goto try_again; + } + + type = (s != NULL) ? AUDIT_RMW_TYPE_MESSAGE : AUDIT_RMW_TYPE_HEARTBEAT; + AUDIT_RMW_PACK_HEADER (header, 0, type, len, sequence_id); + +#ifdef USE_GSSAPI + if (USE_GSS) { + if (send_msg_gss (header, s, len)) { + stop_transport (); + goto try_again; + } + } else +#endif + if (send_msg_tcp (header, s, len)) { + stop_transport (); + goto try_again; + } + +#ifdef USE_GSSAPI + if (USE_GSS) { + if (recv_msg_gss (header, msg, &rlen)) { + stop_transport (); + goto try_again; + } + } else +#endif + if (recv_msg_tcp (header, msg, &rlen)) { + stop_transport (); + goto try_again; + } + + AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq); + msg[rlen] = 0; + + /* Handle this first. It doesn't matter if seq compares or not + * since the other end is going down...deal with it. */ + if (type == AUDIT_RMW_TYPE_ENDING) + return remote_server_ending_handler (msg); + + if (seq != sequence_id) { + /* FIXME: should we read another header and + see if it matches? If so, we need to deal + with timeouts. */ + if (sync_error_handler ("mismatched response")) + return -1; + stop_transport(); + goto try_again; + } + + /* Specific errors we know how to deal with. */ + if (type == AUDIT_RMW_TYPE_DISKLOW) + return remote_disk_low_handler (msg); + if (type == AUDIT_RMW_TYPE_DISKFULL) + return remote_disk_full_handler (msg); + if (type == AUDIT_RMW_TYPE_DISKERROR) + return remote_disk_error_handler (msg); + + /* Generic errors. */ + if (type & AUDIT_RMW_TYPE_FATALMASK) + return generic_remote_error_handler (msg); + if (type & AUDIT_RMW_TYPE_WARNMASK) + return generic_remote_warning_handler (msg); + + return 0; +} + +static int relay_sock(const char *s, size_t len) +{ + int rc; + + switch (config.format) + { + case F_MANAGED: + rc = relay_sock_managed (s, len); + break; + case F_ASCII: + rc = relay_sock_ascii (s, len); + break; + default: + rc = -1; + break; + } + + return rc; +} + +/* Send audit event to remote system */ +static int relay_event(const char *s, size_t len) +{ + int rc; + + switch (config.transport) + { + case T_TCP: + rc = relay_sock(s, len); + break; + default: + rc = -1; + break; + } + + return rc; +} + diff --git a/framework/src/audit/audisp/plugins/remote/audisp-remote.conf b/framework/src/audit/audisp/plugins/remote/audisp-remote.conf new file mode 100644 index 00000000..70d8a992 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/audisp-remote.conf @@ -0,0 +1,31 @@ +# +# This file controls the configuration of the audit remote +# logging subsystem, audisp-remote. +# + +remote_server = +port = 60 +##local_port = +transport = tcp +queue_file = /var/spool/audit/remote.log +mode = immediate +queue_depth = 2048 +format = managed +network_retry_time = 1 +max_tries_per_record = 3 +max_time_per_record = 5 +heartbeat_timeout = 0 + +network_failure_action = stop +disk_low_action = ignore +disk_full_action = ignore +disk_error_action = syslog +remote_ending_action = reconnect +generic_error_action = syslog +generic_warning_action = syslog +overflow_action = syslog + +##enable_krb5 = no +##krb5_principal = +##krb5_client_name = auditd +##krb5_key_file = /etc/audisp/audisp-remote.key diff --git a/framework/src/audit/audisp/plugins/remote/audisp-remote.conf.5 b/framework/src/audit/audisp/plugins/remote/audisp-remote.conf.5 new file mode 100644 index 00000000..55efabfc --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/audisp-remote.conf.5 @@ -0,0 +1,211 @@ +.TH AUDISP-REMOTE.CONF: "5" "Mar 2011" "Red Hat" "System Administration Utilities" +.SH NAME +audisp-remote.conf \- the audisp-remote configuration file +.SH DESCRIPTION +\fBaudisp-remote.conf\fP is the file that controls the configuration of the audit remote logging subsystem. The options that are available are as follows: + +.TP +.I remote_server +This is a one word character string that is the remote server hostname or address that this plugin will send log information to. This can be the numeric address or a resolvable hostname. +.TP +.I port +This option is an unsigned integer that indicates what port to connect to on the remote machine. +.TP +.I local_port +This option is an unsigned integer that indicates what local port to +connect from on the local machine. If unspecified (the default) or +set to the word +.I any +then any available unpriviledged port is used. This is a security mechanism to prevent untrusted user space apps from injecting events into the audit daemon. You should set it to an unused port < 1024 to ensure that only privileged users can bind to that port. Then also set the tcp_client_ports in the aggregating auditd.conf file to match the ports that clients are sending from. +.TP +.I transport +This parameter tells the remote logging app how to send events to the remote system. The only valid value right now is +.IR tcp ". +If set to +.IR tcp , +the remote logging app will just make a normal clear text connection to the remote system. This is not used if kerberos is enabled. +.TP +.I mode +This parameter tells the remote logging app what strategy to use getting records to the remote system. Valid values are +.IR immediate ", and " forward " . +If set to +.IR immediate , +the remote logging app will attempt to send events immediately after getting them. +.I forward +means that it will store the events to disk and then attempt to send the records. If the connection cannot be made, it will queue records until it can connect to the remote system. The depth of the queue is controlled by the +.I queue_depth +option. +.TP +.I queue_file +Path of a file used for the event queue if +.I mode +is set to \fIforward\fP. The default is \fB/var/spool/audit/remote.log\fP. +.TP +.I queue_depth +This option is an unsigned integer that determines how many records can be buffered to disk or in memory before considering it to be a failure sending. This parameter affects the +.I forward +mode of the +.I mode +option and internal queueing for temporary network outtages. The default depth is 2048. +.TP +.I format +This parameter tells the remote logging app what data format will be +used for the messages sent over the network. The default is +.I managed +which adds some overhead to ensure each message is properly handled on +the remote end, and to receive status messages from the remote server. +If +.I ascii +is given instead, each message is a simple ASCII text line with no +overhead at all. If +.I mode +is set to \fIforward\fP, +.I format +must be \fImanaged\fP. +.TP +.I network_retry_time +The time, in seconds, between retries when a network error is +detected. Note that this pause applies starting after the second +attempt, so as to avoid unneeded delays if a reconnect is sufficient +to fix the problem. The default is 1 second. +.TP +.I max_tries_per_record +The maximum number of times an attempt is made to deliver each +message. The minimum value is one, as even a completely successful +delivery requires at least one try. If too many attempts are made, +the network_failure_action action is performed. The default is 3. +.TP +.I max_time_per_record +The maximum amount of time, in seconds, spent attempting to deliver +each message. Note that both this and +.I max_tries_per_record +should be set, as each try may take a long time to time out. The +default value is 5 seconds. If too much time is used on a message, +the network_failure_action action is performed. +.TP +.I heartbeat_timeout +This parameter determines how often in seconds the client should send a heartbeat event to the remote server. This is used to let both the client and server know that each end is alive and has not terminated in a way that it did not shutdown the connection uncleanly. This value must be coordinated with the server's +.I tcp_client_max_idle +setting. The default value is 0 which disables sending a heartbeat. +.TP +.I network_failure_action +This parameter tells the system what action to take whenever there is an error +detected when sending audit events to the remote system. Valid values are +.IR ignore ", " syslog ", " exec ", " suspend ", " single ", " halt ", and " stop . +If set to +.IR ignore , +the remote logging app does nothing. +.I Syslog +means that it will issue a warning to syslog. This is the default. +.I exec +/path-to-script will execute the script. You cannot pass parameters to the script. +.I Suspend +will cause the remote logging app to stop sending records to the remote system. The logging app will still be alive. The +.I single +option will cause the remote logging app to put the computer system in single user mode. The +.I stop +option will cause the remote logging app to exit, but leave other plugins running. The +.I halt +option will cause the remote logging app to shutdown the computer system. +.TP +.I disk_low_action +Likewise, this parameter tells the system what action to take if the +remote end signals a disk low error. The default is to ignore it. +.TP +.I disk_full_action +Likewise, this parameter tells the system what action to take if the +remote end signals a disk full error. The default is to ignore it. +.TP +.I disk_error_action +Likewise, this parameter tells the system what action to take if the +remote end signals a disk error. The default is to log it to syslog. +.TP +.I remote_ending_action +Likewise, this parameter tells the system what action to take if the +remote end signals a disk error. This action has one additional option, +.I reconnect +which tells the remote plugin to attempt to reconnect to the server upon receipt of the next audit record. If it is unsuccessful, the audit record could be lost. The default is to reconnect. +.TP +.I generic_error_action +Likewise, this parameter tells the system what action to take if the +remote end signals an error we don't recognize. The default is to log +it to syslog. +.TP +.I generic_warning_action +Likewise, this parameter tells the system what action to take if the +remote end signals a warning we don't recognize. The default is to +log it to syslog. +.TP +.I queue_error_action +Likewise, this parameter tells the system what action to take if there +is a problem working with a local record queue. The default is to exit. +.TP +.I overflow_action +This parameter tells the system what action to take if the +internal event queue overflows. Valid values are +.IR ignore ", " syslog ", " suspend ", " single ", and " halt " . +If set to +.IR ignore , +the remote logging app does nothing. +.I Syslog +means that it will issue a warning to syslog. This is the default. +.I Suspend +will cause the remote logging app to stop sending records to the remote system. The logging app will still be alive. The +.I single +option will cause the remote logging app to put the computer system in single user mode. The +.I halt +option will cause the remote logging app to shutdown the computer system. +.TP +.I enable_krb5 +If set to "yes", Kerberos 5 will be used for authentication and +encryption. Default is "no". Note that encryption can only be used +with managed connections, not plain ASCII. +.TP +.I krb5_principal +If specified, This is the expected principal for the server. The +client and server will use the specified principal to negotiate the +encryption. The format for the +.I krb5_principal +is like somename/hostname, see the auditd.conf man page for +details. If not specified, the krb5_client_name and remote_server values +are used. +.TP +.I krb5_client_name +This specifies the name portion of the client's own principal. If +unspecified, the default is "auditd". The remainder of the principal +will consist of the host's fully qualified domain name and the default +Kerberos realm, like this: +.I auditd/host14.example.com@EXAMPLE.COM +(assuming you gave "auditd" as the krb_client_name). Note that the +client and server must have the same principal name and realm. +.TP +.I krb5_key_file +Location of the key for this client's principal. +Note that the key file must be owned by root and mode 0400. +The default is +.I /etc/audisp/audisp-remote.key + + +.SH "NOTES" +Specifying a local port may make it difficult to restart the audit +subsystem due to the previous connection being in a TIME_WAIT state, +if you're reconnecting to and from the same hosts and ports as before. + +The network failure logic works as follows: The first attempt to +deliver normally "just works". If it doesn't, a second attempt is +immediately made, perhaps after reconnecting to the server. If +the second attempt also fails, +.I audispd-remote +pauses for the configured time and tries again. It continues to pause +and retry until either too many attempts have been made or the allowed +time expires. Note that these times govern the maximum amount of time +the remote server is allowed in order to reboot, if you want to +maintain logging across a reboot. + +.SH "SEE ALSO" +.BR audispd (8), +.BR audisp-remote(8), +.BR auditd.conf(5). +.SH AUTHOR +Steve Grubb + diff --git a/framework/src/audit/audisp/plugins/remote/notes.txt b/framework/src/audit/audisp/plugins/remote/notes.txt new file mode 100644 index 00000000..1cd46193 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/notes.txt @@ -0,0 +1,31 @@ +The queue data structure can keep data only in memory, only on disk +(writing it to disk and reading from disk), or in both (writing everything +to disk, but reading from disk only data stored in a previous run). +audisp-remote will use the last option for performance. + +The queue file format starts with a fixed header, followed by an array +of slots for strings. Due to the fixed size of each slot the file format +is rather inefficient, but it is also very simple. + +The file is preallocated and the string slots will be aligned to a 4KB +boundary, so it should be necessary to only write one block to disk +when audisp-remote receives a (short) audit record. + +With the default queue size of 200 items the file will be about 2.4 +megabytes large, which is probably not really worth worrying about. + +If necessary, the space utilization could be improved by storing strings +consecutively instead of using pre-arranged slots. + +The queue file format is intended to be resilient against unexpected +termination of the process, and should be resilient against unexpected +system crash as long as the OS does not reorder writes (the string data +is written before the header that indicates that it is present) - but +ultimately resiliency against such failures is limited by other +links in the audit record transmission chain - if the record is lost +within auditd or audispd, having a resilient queue file format does +not help; audit records generated within the kernel are necessarily +lost if the system crashes before they are read by auditd because +the kernel will not be able to regenerate/retransmit them after the next +boot. + diff --git a/framework/src/audit/audisp/plugins/remote/queue.c b/framework/src/audit/audisp/plugins/remote/queue.c new file mode 100644 index 00000000..971e4e46 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/queue.c @@ -0,0 +1,574 @@ +/* queue.c - a string queue implementation + * Copyright 2009, 2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Miloslav Trmač + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "queue.h" + +struct queue +{ + int flags; /* Q_* */ + int fd; /* -1 if !Q_IN_FILE */ + /* NULL if !Q_IN_MEMORY. [i] contains a memory copy of the queue entry + "i", if known - it may be NULL even if entry exists. */ + unsigned char **memory; + size_t num_entries; + size_t entry_size; + size_t queue_head; + size_t queue_length; + unsigned char buffer[]; /* Used only locally within q_peek() */ +}; + +/* Infrastructure */ + +/* Compile-time expression verification */ +#define verify(E) do { \ + char verify__[(E) ? 1 : -1]; \ + (void)verify__; \ + } while (0) + +/* Like pread(), except that it handles partial reads, and returns 0 on + success. */ +static int full_pread(int fd, void *buf, size_t size, off_t offset) +{ + while (size != 0) { + ssize_t run, res; + + if (size > SSIZE_MAX) + run = SSIZE_MAX; + else + run = size; + res = pread(fd, buf, run, offset); + if (res < 0) + return -1; + if (res == 0) { + errno = ENXIO; /* Any better value? */ + return -1; + } + buf = (unsigned char *)buf + res; + size -= res; + offset += res; + } + return 0; +} + +/* Like pwrite(), except that it handles partial writes, and returns 0 on + success. */ +static int full_pwrite(int fd, const void *buf, size_t size, off_t offset) +{ + while (size != 0) { + ssize_t run, res; + + if (size > SSIZE_MAX) + run = SSIZE_MAX; + else + run = size; + res = pwrite(fd, buf, run, offset); + if (res < 0) + return -1; + if (res == 0) { + errno = ENXIO; /* Any better value? */ + return -1; + } + buf = (const unsigned char *)buf + res; + size -= res; + offset += res; + } + return 0; +} + +/* File format and utilities */ + +/* The mutable part of struct file_header */ +struct fh_state { + uint32_t queue_head; /* 0-based index of the first non-empty entry */ + uint32_t queue_length; /* [0, num_entries] */ +}; + +/* All integer values are in network byte order (big endian) */ +struct file_header +{ + uint8_t magic[14]; /* See fh_magic below */ + uint8_t version; /* File format version, see FH_VERSION* below */ + uint8_t reserved; /* Must be 0 */ + /* Total file size is (num_entries + 1) * entry_size. This must fit + into SIZE_MAX because the "len" parameter of posix_fallocate has + a size_t type. */ + uint32_t num_entries; /* Total number of entries allocated */ + uint32_t entry_size; + struct fh_state s; +}; + +/* Contains a '\0' byte to unambiguously mark the file as a binary file. */ +static const uint8_t fh_magic[14] = "\0audisp-remote"; +#define FH_VERSION_0 0x00 + +/* Return file position for ENTRY in Q */ +static size_t entry_offset (const struct queue *q, size_t entry) +{ + return (entry + 1) * q->entry_size; +} + +/* Synchronize Q if required and return 0. + On error, return -1 and set errno. */ +static int q_sync(struct queue *q) +{ + if ((q->flags & Q_SYNC) == 0) + return 0; + return fdatasync(q->fd); +} + +/* Sync file's fh_state with Q, q_sync (Q), and return 0. + On error, return -1 and set errno. */ +static int sync_fh_state (struct queue *q) +{ + struct fh_state s; + + if (q->fd == -1) + return 0; + + s.queue_head = htonl(q->queue_head); + s.queue_length = htonl(q->queue_length); + if (full_pwrite(q->fd, &s, sizeof(s), offsetof(struct file_header, s)) + != 0) + return -1; + return q_sync(q); +} + +/* Queue implementation */ + +/* Open PATH for Q, update Q from it, and return 0. + On error, return -1 and set errno; Q->fd may be set even on error. */ +static int q_open_file(struct queue *q, const char *path) +{ + int open_flags, fd_flags; + struct stat st; + struct file_header fh; + + open_flags = O_RDWR; + if ((q->flags & Q_CREAT) != 0) + open_flags |= O_CREAT; + if ((q->flags & Q_EXCL) != 0) + open_flags |= O_EXCL; + q->fd = open(path, open_flags, S_IRUSR | S_IWUSR); + if (q->fd == -1) + return -1; + + fd_flags = fcntl(q->fd, F_GETFD); + if (fd_flags < 0) + return -1; + if (fcntl(q->fd, F_SETFD, fd_flags | FD_CLOEXEC) == -1) + return -1; + + /* File locking in POSIX is pretty much broken... let's hope nobody + attempts to open a single file twice within the same process. + open() above has initialized the file offset to 0, so the lockf() + below affects the whole file. */ + if (lockf(q->fd, F_TLOCK, 0) != 0) { + if (errno == EACCES || errno == EAGAIN) + errno = EBUSY; /* This makes more sense... */ + return -1; + } + + if (fstat(q->fd, &st) != 0) + return -1; + if (st.st_size == 0) { + verify(sizeof(fh.magic) == sizeof(fh_magic)); + memcpy(fh.magic, fh_magic, sizeof(fh.magic)); + fh.version = FH_VERSION_0; + fh.reserved = 0; + fh.num_entries = htonl(q->num_entries); + fh.entry_size = htonl(q->entry_size); + fh.s.queue_head = htonl(0); + fh.s.queue_length = htonl(0); + if (full_pwrite(q->fd, &fh, sizeof(fh), 0) != 0) + return -1; + if (q_sync(q) != 0) + return -1; +#ifdef HAVE_POSIX_FALLOCATE + if (posix_fallocate(q->fd, 0, + (q->num_entries + 1) * q->entry_size) != 0) + return -1; +#endif + } else { + uint32_t file_entries; + if (full_pread(q->fd, &fh, sizeof(fh), 0) != 0) + return -1; + if (memcmp(fh.magic, fh_magic, sizeof(fh.magic)) != 0 + || fh.version != FH_VERSION_0 || fh.reserved != 0 + || fh.entry_size != htonl(q->entry_size)) { + errno = EINVAL; + return -1; + } + file_entries = ntohl(fh.num_entries); + if (file_entries > SIZE_MAX / q->entry_size - 1 + || ((uintmax_t)st.st_size + != (file_entries + 1) * q->entry_size)) { + errno = EINVAL; + return -1; + } + } + /* Note that this may change q->num_entries! */ + q->num_entries = ntohl(fh.num_entries); + q->queue_head = ntohl(fh.s.queue_head); + q->queue_length = ntohl(fh.s.queue_length); + if (q->queue_head >= q->num_entries + || q->queue_length > q->num_entries) { + errno = EINVAL; + return -1; + } + return 0; +} + +/* Like q_open(), but does not handle Q_RESIZE, and NUM_ENTRIES is only used + when creating a new file. */ +static struct queue *q_open_no_resize(int q_flags, const char *path, + size_t num_entries, size_t entry_size) +{ + struct queue *q; + int saved_errno; + + if ((q_flags & (Q_IN_MEMORY | Q_IN_FILE)) == 0) { + errno = EINVAL; + return NULL; + } + if (num_entries == 0 || num_entries > UINT32_MAX + || entry_size < 1 /* for trailing NUL */ + || entry_size < sizeof(struct file_header) /* for Q_IN_FILE */ + /* to allocate "struct queue" including its buffer*/ + || entry_size > UINT32_MAX - sizeof(struct queue)) { + errno = EINVAL; + return NULL; + } + if (entry_size > SIZE_MAX + || num_entries > SIZE_MAX / entry_size - 1 /* for Q_IN_FILE */ + || num_entries > SIZE_MAX / sizeof(*q->memory)) { + errno = EINVAL; + return NULL; + } + + q = malloc(sizeof(*q) + entry_size); + if (q == NULL) + return NULL; + q->flags = q_flags; + q->fd = -1; + q->memory = NULL; + q->num_entries = num_entries; + q->entry_size = entry_size; + q->queue_head = 0; + q->queue_length = 0; + + if ((q_flags & Q_IN_MEMORY) != 0) { + size_t sz = num_entries * sizeof(*q->memory); + + q->memory = malloc(sz); + if (q->memory == NULL) + goto err; + memset(q->memory, 0, sz); + } + + if ((q_flags & Q_IN_FILE) != 0 && q_open_file(q, path) != 0) + goto err; + + return q; + +err: + saved_errno = errno; + if (q->fd != -1) + close(q->fd); + free(q->memory); + free(q); + errno = saved_errno; + return NULL; +} + +void q_close(struct queue *q) +{ + if (q->fd != -1) + close(q->fd); /* Also releases the file lock */ + if (q->memory != NULL) { + size_t i; + + for (i = 0; i < q->num_entries; i++) + free(q->memory[i]); + free(q->memory); + } + free(q); +} + +/* Internal use only: add DATA to Q, but don't update fh_state. */ +static int q_append_no_sync_fh_state(struct queue *q, const char *data) +{ + size_t data_size, entry_index; + unsigned char *copy; + + if (q->queue_length == q->num_entries) { + errno = ENOSPC; + return -1; + } + + data_size = strlen(data) + 1; + if (data_size > q->entry_size) { + errno = EINVAL; + return -1; + } + + entry_index = (q->queue_head + q->queue_length) % q->num_entries; + if (q->memory != NULL) { + if (q->memory[entry_index] != NULL) { + errno = EIO; /* This is _really_ unexpected. */ + return -1; + } + copy = malloc(data_size); + if (copy == NULL) + return -1; + memcpy(copy, data, data_size); + } else + copy = NULL; + + if (q->fd != -1) { + size_t offset; + + offset = entry_offset(q, entry_index); + if (full_pwrite(q->fd, data, data_size, offset) != 0) { + int saved_errno; + + saved_errno = errno; + if (copy != NULL) + free(copy); + errno = saved_errno; + return -1; + } + } + + if (copy != NULL) + q->memory[entry_index] = copy; + + q->queue_length++; + + return 0; +} + +int q_append(struct queue *q, const char *data) +{ + int r; + + r = q_append_no_sync_fh_state(q, data); + if (r != 0) + return r; + + return sync_fh_state(q); /* Calls q_sync() */ +} + +int q_peek(struct queue *q, char *buf, size_t size) +{ + const unsigned char *data; + size_t data_size; + + if (q->queue_length == 0) + return 0; + + if (q->memory != NULL && q->memory[q->queue_head] != NULL) { + data = q->memory[q->queue_head]; + data_size = strlen((char *)data) + 1; + } else if (q->fd != -1) { + const unsigned char *end; + + if (full_pread(q->fd, q->buffer, q->entry_size, + entry_offset(q, q->queue_head)) != 0) + return -1; + data = q->buffer; + end = memchr(q->buffer, '\0', q->entry_size); + if (end == NULL) { + /* FIXME: silently drop this entry? */ + errno = EBADMSG; + return -1; + } + data_size = (end - data) + 1; + + if (q->memory != NULL) { + unsigned char *copy; + + copy = malloc(data_size); + if (copy != NULL) { /* Silently ignore failures. */ + memcpy(copy, data, data_size); + q->memory[q->queue_head] = copy; + } + } + } else { + errno = EIO; /* This is _really_ unexpected. */ + return -1; + } + + if (size < data_size) { + errno = ERANGE; + return -1; + } + memcpy(buf, data, data_size); + return data_size; +} + +/* Internal use only: drop head of Q, but don't write this into the file */ +static int q_drop_head_memory_only(struct queue *q) +{ + if (q->queue_length == 0) { + errno = EINVAL; + return -1; + } + + if (q->memory != NULL) { + free(q->memory[q->queue_head]); + q->memory[q->queue_head] = NULL; + } + + q->queue_head++; + if (q->queue_head == q->num_entries) + q->queue_head = 0; + q->queue_length--; + return 0; +} + +int q_drop_head(struct queue *q) +{ + int r; + + r = q_drop_head_memory_only(q); + if (r != 0) + return r; + + return sync_fh_state(q); /* Calls q_sync() */ +} + +size_t q_queue_length(const struct queue *q) +{ + return q->queue_length; +} + +struct queue *q_open(int q_flags, const char *path, size_t num_entries, + size_t entry_size) +{ + struct queue *q, *q2; + char *tmp_path, *buf; + size_t path_len; + int saved_errno, fd; + + q = q_open_no_resize(q_flags, path, num_entries, entry_size); + if (q == NULL || q->num_entries == num_entries) + return q; + + if ((q->flags & Q_RESIZE) == 0) { + saved_errno = EINVAL; + goto err_errno_q; + } + + if (q->queue_length > num_entries) { + saved_errno = ENOSPC; + goto err_errno_q; + } + + buf = malloc(entry_size); + if (buf == NULL) { + saved_errno = errno; + goto err_errno_q; + } + + path_len = strlen(path); + tmp_path = malloc(path_len + 7); + if (tmp_path == NULL) { + saved_errno = errno; + goto err_errno_buf; + } + memcpy(tmp_path, path, path_len); + memcpy(tmp_path + path_len, "XXXXXX", 7); + /* We really want tmpnam() here (safe due to the Q_EXCL below), but gcc + warns on any use of tmpnam(). */ + fd = mkstemp(tmp_path); + if (fd == -1) { + saved_errno = errno; + goto err_errno_tmp_path; + } + if (close(fd) != 0 || unlink(tmp_path) != 0) { + saved_errno = errno; + goto err_errno_tmp_file; + } + + q2 = q_open_no_resize(q_flags | Q_CREAT | Q_EXCL, tmp_path, num_entries, + entry_size); + if (q2 == NULL) { + saved_errno = errno; + goto err_errno_tmp_file; + } + if (q2->num_entries != num_entries) { + errno = EIO; /* This is _really_ unexpected. */ + goto err_q2; + } + + for (;;) { + int r; + + r = q_peek(q, buf, entry_size); + if (r == 0) + break; + if (r < 0) + goto err_q2; + + if (q_append_no_sync_fh_state(q2, buf) != 0) + goto err_q2; + if (q_drop_head_memory_only(q) != 0) + goto err_q2; + } + if (sync_fh_state(q2) != 0) + goto err_q2; + + if (rename(tmp_path, path) != 0) + goto err_q2; + + q_close(q); + free(buf); + free(tmp_path); + return q2; + +err_q2: + saved_errno = errno; + q_close(q2); +err_errno_tmp_file: + unlink(tmp_path); +err_errno_tmp_path: + free(tmp_path); +err_errno_buf: + free(buf); +err_errno_q: + q_close(q); + errno = saved_errno; + return NULL; +} diff --git a/framework/src/audit/audisp/plugins/remote/queue.h b/framework/src/audit/audisp/plugins/remote/queue.h new file mode 100644 index 00000000..5f5ef647 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/queue.h @@ -0,0 +1,66 @@ +/* queue.h -- a queue abstraction + * Copyright 2009, 2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Miloslav Trmač + */ + +#ifndef QUEUE_HEADER +#define QUEUE_HEADER + +#include + +struct queue; + +enum { + // Queue storage. Both options can be set at the same time. + Q_IN_MEMORY = 1 << 0, // Keep a copy of the queue in memory + Q_IN_FILE = 1 << 1, // Store the queue in a file + // Other flags for use with Q_IN_FILE + Q_CREAT = 1 << 2, // Create the queue if it does not exist + Q_EXCL = 1 << 3, // With Q_CREAT, don't open an existing queue + Q_SYNC = 1 << 4, // fdatasync() after each operation + Q_RESIZE = 1 << 5, // resize the queue if needed +}; + +/* Open a queue using Q_FLAGS and return it. If Q_IN_FILE: use PATH for the + * file, NUM_ENTRIES must be the same for all users of the file unless Q_RESIZE + * is set. ENTRY_SIZE is the maximum length of a stored string, including the + * trailing NUL. If Q_IN_FILE, it must be the same for all users of the file. + * On error, return NULL and set errno. */ +struct queue *q_open(int q_flags, const char *path, size_t num_entries, + size_t entry_size); +/* Close Q. */ +void q_close(struct queue *q); + +/* Add DATA to tail of Q. Return 0 on success, -1 on error and set errno. */ +int q_append(struct queue *q, const char *data); + +/* Peek at head of Q, storing it into BUF of SIZE. Return 1 if an entry + * exists, 0 if queue is empty. On error, return -1 and set errno. */ +int q_peek(struct queue *q, char *buf, size_t size); + +/* Drop head of Q and return 0. On error, return -1 and set errno. */ +int q_drop_head(struct queue *q); + +/* Return the number of entries in Q. */ +size_t q_queue_length(const struct queue *q); + +#endif + diff --git a/framework/src/audit/audisp/plugins/remote/remote-config.c b/framework/src/audit/audisp/plugins/remote/remote-config.c new file mode 100644 index 00000000..841a1ed3 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/remote-config.c @@ -0,0 +1,780 @@ +/* remote-config.c -- + * Copyright 2008,2009,2011,2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "remote-config.h" + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; + const char *option; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(struct nv_pair *, int, remote_conf_t *); + int max_options; +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *f, char *buf); +static int nv_split(char *buf, struct nv_pair *nv); +static const struct kw_pair *kw_lookup(const char *val); +static int server_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int port_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int local_port_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int transport_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int mode_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int queue_file_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int depth_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int format_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int heartbeat_timeout_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int enable_krb5_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int krb5_principal_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int krb5_client_name_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int krb5_key_file_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int network_retry_time_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int max_tries_per_record_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int max_time_per_record_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +#define AP(x) static int x##_action_parser(struct nv_pair *nv, int line, \ + remote_conf_t *config); +AP(network_failure) +AP(disk_low) +AP(disk_full) +AP(disk_error) +AP(generic_error) +AP(generic_warning) +AP(queue_error) +#undef AP +static int remote_ending_action_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int overflow_action_parser(struct nv_pair *nv, int line, + remote_conf_t *config); +static int sanity_check(remote_conf_t *config, const char *file); + +static const struct kw_pair keywords[] = +{ + {"remote_server", server_parser, 0 }, + {"port", port_parser, 0 }, + {"local_port", local_port_parser, 0 }, + {"transport", transport_parser, 0 }, + {"mode", mode_parser, 0 }, + {"queue_file", queue_file_parser, 0 }, + {"queue_depth", depth_parser, 0 }, + {"format", format_parser, 0 }, + {"network_retry_time", network_retry_time_parser, 0 }, + {"max_tries_per_record", max_tries_per_record_parser, 0 }, + {"max_time_per_record", max_time_per_record_parser, 0 }, + {"heartbeat_timeout", heartbeat_timeout_parser, 0 }, + {"enable_krb5", enable_krb5_parser, 0 }, + {"krb5_principal", krb5_principal_parser, 0 }, + {"krb5_client_name", krb5_client_name_parser, 0 }, + {"krb5_key_file", krb5_key_file_parser, 0 }, + {"network_failure_action", network_failure_action_parser, 1 }, + {"disk_low_action", disk_low_action_parser, 1 }, + {"disk_full_action", disk_full_action_parser, 1 }, + {"disk_error_action", disk_error_action_parser, 1 }, + {"remote_ending_action", remote_ending_action_parser, 1 }, + {"generic_error_action", generic_error_action_parser, 1 }, + {"generic_warning_action", generic_warning_action_parser, 1 }, + {"queue_error_action", queue_error_action_parser, 1 }, + {"overflow_action", overflow_action_parser, 1 }, + { NULL, NULL, 0 } +}; + +static const struct nv_list transport_words[] = +{ + {"tcp", T_TCP }, + { NULL, 0 } +}; + +static const struct nv_list mode_words[] = +{ + {"immediate", M_IMMEDIATE }, + {"forward", M_STORE_AND_FORWARD }, + { NULL, 0 } +}; + +static const struct nv_list fail_action_words[] = +{ + {"ignore", FA_IGNORE }, + {"syslog", FA_SYSLOG }, + {"exec", FA_EXEC }, + {"suspend", FA_SUSPEND }, + {"single", FA_SINGLE }, + {"halt", FA_HALT }, + {"stop", FA_STOP }, + { NULL, 0 } +}; + +static const struct nv_list overflow_action_words[] = +{ + {"ignore", OA_IGNORE }, + {"syslog", OA_SYSLOG }, + {"suspend", OA_SUSPEND }, + {"single", OA_SINGLE }, + {"halt", OA_HALT }, + { NULL, 0 } +}; + +static const struct nv_list format_words[] = +{ + {"ascii", F_ASCII }, + {"managed", F_MANAGED }, + { NULL, 0 } +}; + +#ifdef USE_GSSAPI +static const struct nv_list enable_krb5_values[] = +{ + {"yes", 1 }, + {"no", 0 }, + { NULL, 0 } +}; +#endif + +/* + * Set everything to its default value +*/ +void clear_config(remote_conf_t *config) +{ + config->remote_server = NULL; + config->port = 60; + config->local_port = 0; + config->transport = T_TCP; + config->mode = M_IMMEDIATE; + config->queue_file = NULL; + config->queue_depth = 2048; + config->format = F_MANAGED; + + config->network_retry_time = 1; + config->max_tries_per_record = 3; + config->max_time_per_record = 5; + config->heartbeat_timeout = 0; + +#define IA(x,f) config->x##_action = f; config->x##_exe = NULL + IA(network_failure, FA_STOP); + IA(disk_low, FA_IGNORE); + IA(disk_full, FA_IGNORE); + IA(disk_error, FA_SYSLOG); + IA(remote_ending, FA_RECONNECT); + IA(generic_error, FA_SYSLOG); + IA(generic_warning, FA_SYSLOG); + IA(queue_error, FA_STOP); +#undef IA + config->overflow_action = OA_SYSLOG; + + config->enable_krb5 = 0; + config->krb5_principal = NULL; + config->krb5_client_name = NULL; + config->krb5_key_file = NULL; +} + +int load_config(remote_conf_t *config, const char *file) +{ + int fd, rc, mode, lineno = 1; + struct stat st; + FILE *f; + char buf[128]; + + clear_config(config); + + /* open the file */ + mode = O_RDONLY; + rc = open(file, mode); + if (rc < 0) { + if (errno != ENOENT) { + syslog(LOG_ERR, "Error opening %s (%s)", file, + strerror(errno)); + return 1; + } + syslog(LOG_WARNING, + "Config file %s doesn't exist, skipping", file); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + if (fstat(fd, &st) < 0) { + syslog(LOG_ERR, "Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + syslog(LOG_ERR, "Error - %s isn't owned by root", + file); + close(fd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + syslog(LOG_ERR, "Error - %s is world writable", + file); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + syslog(LOG_ERR, "Error - %s is not a regular file", + file); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + syslog(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf)) { + // convert line into name-value pair + const struct kw_pair *kw; + struct nv_pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + syslog(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, file); + break; + case 2: // no '=' sign + syslog(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, file); + break; + default: // something else went wrong... + syslog(LOG_ERR, + "Unknown error for line %d in %s", + lineno, file); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + syslog(LOG_ERR, + "Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, file); + fclose(f); + return 1; + } + + /* Check number of options */ + if (kw->max_options == 0 && nv.option != NULL) { + syslog(LOG_ERR, + "Keyword \"%s\" has invalid option " + "\"%s\" in line %d of %s", + nv.name, nv.option, lineno, file); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(&nv, lineno, config); + if (rc != 0) { + fclose(f); + return 1; // local parser puts message out + } + + lineno++; + } + + fclose(f); + if (lineno > 1) + return sanity_check(config, file); + return 0; +} + +static char *get_line(FILE *f, char *buf) +{ + if (fgets_unlocked(buf, 128, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) + *ptr = 0; + return buf; + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr, *saved; + + nv->name = NULL; + nv->value = NULL; + nv->option = NULL; + ptr = strtok_r(buf, " ", &saved); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* See if there's an option */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) { + nv->option = ptr; + + /* Make sure there's nothing else */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) + return 1; + } + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int check_exe_name(const char *val, int line) +{ + struct stat buf; + + if (val == NULL) { + syslog(LOG_ERR, "Executable path needed for line %d", line); + return -1; + } + + if (*val != '/') { + syslog(LOG_ERR, "Absolute path needed for %s - line %d", + val, line); + return -1; + } + + if (stat(val, &buf) < 0) { + syslog(LOG_ERR, "Unable to stat %s (%s) - line %d", val, + strerror(errno), line); + return -1; + } + if (!S_ISREG(buf.st_mode)) { + syslog(LOG_ERR, "%s is not a regular file - line %d", val, + line); + return -1; + } + if (buf.st_uid != 0) { + syslog(LOG_ERR, "%s is not owned by root - line %d", val, + line); + return -1; + } + if ((buf.st_mode & (S_IRWXU|S_IRWXG|S_IWOTH)) != + (S_IRWXU|S_IRGRP|S_IXGRP)) { + syslog(LOG_ERR, "%s permissions should be 0750 - line %d", val, + line); + return -1; + } + return 0; +} + +static int server_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + if (nv->value) + config->remote_server = strdup(nv->value); + else + config->remote_server = NULL; + return 0; +} + +static int parse_uint (const struct nv_pair *nv, int line, unsigned int *valp, + unsigned int min, unsigned int max) +{ + const char *ptr = nv->value; + unsigned int i; + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + syslog(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + syslog(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range */ + if (min != 0 && i < (int)min) { + syslog(LOG_ERR, + "Error - converted number (%s) is too small - line %d", + nv->value, line); + return 1; + } + if (max != 0 && i > max) { + syslog(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + *valp = (unsigned int)i; + return 0; +} + +static int port_parser(struct nv_pair *nv, int line, remote_conf_t *config) +{ + return parse_uint (nv, line, &(config->port), 0, 65535); +} + +static int local_port_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + if ((strcasecmp(nv->value, "any") == 0)) + return 0; // The default is 0, which means any port + return parse_uint (nv, line, &(config->local_port), 0, 65535); +} + +static int transport_parser(struct nv_pair *nv, int line, remote_conf_t *config) +{ + int i; + for (i=0; transport_words[i].name != NULL; i++) { + if (strcasecmp(nv->value, transport_words[i].name) == 0) { + config->transport = transport_words[i].option; + return 0; + } + } + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int mode_parser(struct nv_pair *nv, int line, remote_conf_t *config) +{ + int i; + for (i=0; mode_words[i].name != NULL; i++) { + if (strcasecmp(nv->value, mode_words[i].name) == 0) { + config->mode = mode_words[i].option; + return 0; + } + } + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int queue_file_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + if (nv->value) { + if (*nv->value != '/') { + syslog(LOG_ERR, "Absolute path needed for %s - line %d", + nv->value, line); + return 1; + } + config->queue_file = strdup(nv->value); + } else + config->queue_file = NULL; + return 0; +} + +static int depth_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + return parse_uint (nv, line, &(config->queue_depth), 1, INT_MAX); +} + +static int action_parser(struct nv_pair *nv, int line, + failure_action_t *actp, const char **exep) +{ + int i; + for (i=0; fail_action_words[i].name != NULL; i++) { + if (strcasecmp(nv->value, fail_action_words[i].name) == 0) { + if (fail_action_words[i].option == FA_EXEC) { + if (check_exe_name(nv->option, line)) + return 1; + *exep = strdup(nv->option); + } + *actp = fail_action_words[i].option; + return 0; + } + } + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +#define AP(x) \ +static int x##_action_parser(struct nv_pair *nv, int line, \ + remote_conf_t *config) \ +{ \ + return action_parser(nv,line,&(config->x##_action),&(config->x##_exe));\ +} \ + +AP(network_failure) +AP(disk_low) +AP(disk_full) +AP(disk_error) +AP(generic_error) +AP(generic_warning) +AP(queue_error) +#undef AP + +static int overflow_action_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + int i; + + for (i=0; overflow_action_words[i].name != NULL; i++) { + if (strcasecmp(nv->value, overflow_action_words[i].name) == 0) { + config->overflow_action = overflow_action_words[i].option; + return 0; + } + } + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int remote_ending_action_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + if (strcasecmp(nv->value, "reconnect") == 0) { + config->remote_ending_action = FA_RECONNECT; + return 0; + } + return action_parser(nv, line, &config->remote_ending_action, + &config->remote_ending_exe); +} + +static int format_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + int i; + for (i=0; format_words[i].name != NULL; i++) { + if (strcasecmp(nv->value, format_words[i].name) == 0) { + config->format = format_words[i].option; + return 0; + } + } + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int network_retry_time_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + return parse_uint(nv, line, &config->network_retry_time, 1, INT_MAX); +} + +static int max_tries_per_record_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + return parse_uint(nv, line, &config->max_tries_per_record, 1, INT_MAX); +} + +static int max_time_per_record_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + return parse_uint(nv, line, &(config->max_time_per_record), 1, INT_MAX); +} + +static int heartbeat_timeout_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ + return parse_uint (nv, line, &(config->heartbeat_timeout), 0, INT_MAX); +} + +static int enable_krb5_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ +#ifndef USE_GSSAPI + syslog(LOG_INFO, + "GSSAPI support is not enabled, ignoring value at line %d", + line); + return 0; +#else + unsigned long i; + + for (i=0; enable_krb5_values[i].name != NULL; i++) { + if (strcasecmp(nv->value, enable_krb5_values[i].name) == 0) { + config->enable_krb5 = enable_krb5_values[i].option; + return 0; + } + } + syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +#endif +} + +static int krb5_principal_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ +#ifndef USE_GSSAPI + syslog(LOG_INFO, + "GSSAPI support is not enabled, ignoring value at line %d", + line); +#else + if (config->krb5_principal) + free ((char *)config->krb5_principal); + + config->krb5_principal = strdup(nv->value); +#endif + return 0; +} + +static int krb5_client_name_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ +#ifndef USE_GSSAPI + syslog(LOG_INFO, + "GSSAPI support is not enabled, ignoring value at line %d", + line); +#else + if (config->krb5_client_name) + free ((char *)config->krb5_client_name); + + config->krb5_client_name = strdup(nv->value); +#endif + return 0; +} + +static int krb5_key_file_parser(struct nv_pair *nv, int line, + remote_conf_t *config) +{ +#ifndef USE_GSSAPI + syslog(LOG_INFO, + "GSSAPI support is not enabled, ignoring value at line %d", + line); +#else + if (config->krb5_key_file) + free ((char *)config->krb5_key_file); + + config->krb5_key_file = strdup(nv->value); +#endif + return 0; +} + +/* + * This function is where we do the integrated check of the audispd config + * options. At this point, all fields have been read. Returns 0 if no + * problems and 1 if problems detected. + */ +static int sanity_check(remote_conf_t *config, const char *file) +{ + /* Error checking */ +// server should have string +// port should be less that 32k +// queue_depth should be less than 100k +// If fail_action is F_EXEC, fail_exec must exist + if (config->mode == M_STORE_AND_FORWARD + && config->format != F_MANAGED) { + syslog(LOG_ERR, "\"mode=forward\" is valid only with " + "\"format=managed\""); + return 1; + } + return 0; +} + +void free_config(remote_conf_t *config) +{ + free((void *)config->remote_server); + free((void *)config->queue_file); + free((void *)config->network_failure_exe); + free((void *)config->disk_low_exe); + free((void *)config->disk_full_exe); + free((void *)config->disk_error_exe); + free((void *)config->remote_ending_exe); + free((void *)config->generic_error_exe); + free((void *)config->generic_warning_exe); + free((void *)config->queue_error_exe); + free((void *)config->krb5_principal); + free((void *)config->krb5_client_name); + free((void *)config->krb5_key_file); +} + diff --git a/framework/src/audit/audisp/plugins/remote/remote-config.h b/framework/src/audit/audisp/plugins/remote/remote-config.h new file mode 100644 index 00000000..20c1f0b2 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/remote-config.h @@ -0,0 +1,78 @@ +/* remote-config.h -- + * Copyright 2008, 2009, 2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef REMOTE_CONFIG_H +#define REMOTE_CONFIG_H + +typedef enum { M_IMMEDIATE, M_STORE_AND_FORWARD } rmode_t; +typedef enum { T_TCP, T_SSL, T_GSSAPI, T_LABELED } transport_t; +typedef enum { F_ASCII, F_MANAGED } format_t; +typedef enum { FA_IGNORE, FA_SYSLOG, FA_EXEC, FA_RECONNECT, FA_SUSPEND, + FA_SINGLE, FA_HALT, FA_STOP } failure_action_t; +typedef enum { OA_IGNORE, OA_SYSLOG, OA_SUSPEND, OA_SINGLE, + OA_HALT } overflow_action_t; + +typedef struct remote_conf +{ + const char *remote_server; + unsigned int port; + unsigned int local_port; + transport_t transport; + rmode_t mode; + const char *queue_file; + unsigned int queue_depth; + format_t format; + unsigned int network_retry_time; + unsigned int max_tries_per_record; + unsigned int max_time_per_record; + unsigned int heartbeat_timeout; + int enable_krb5; + const char *krb5_principal; + const char *krb5_client_name; + const char *krb5_key_file; + + failure_action_t network_failure_action; + const char *network_failure_exe; + failure_action_t disk_low_action; + const char *disk_low_exe; + failure_action_t disk_full_action; + const char *disk_full_exe; + failure_action_t disk_error_action; + const char *disk_error_exe; + failure_action_t remote_ending_action; + const char *remote_ending_exe; + failure_action_t generic_error_action; + const char *generic_error_exe; + failure_action_t generic_warning_action; + const char *generic_warning_exe; + failure_action_t queue_error_action; + const char *queue_error_exe; + overflow_action_t overflow_action; +} remote_conf_t; + +void clear_config(remote_conf_t *config); +int load_config(remote_conf_t *config, const char *file); +void free_config(remote_conf_t *config); + +#endif + diff --git a/framework/src/audit/audisp/plugins/remote/remote-fgets.c b/framework/src/audit/audisp/plugins/remote/remote-fgets.c new file mode 100644 index 00000000..41e9c0c2 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/remote-fgets.c @@ -0,0 +1,123 @@ +/* remote-fgets.c -- + * Copyright 2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include "remote-fgets.h" + +#define BUF_SIZE 8192 +static char buffer[2*BUF_SIZE+1] = { 0 }; +static char *current = buffer; +static char *const eptr = buffer+(2*BUF_SIZE); +static int eof = 0; + +int remote_fgets_eof(void) +{ + return eof; +} + +/* Function to check if we have more data stored + * and ready to process. If we have a newline or enough + * bytes we return 1 for success. Otherwise 0 meaning that + * there is not enough to process without blocking. */ +int remote_fgets_more(size_t blen) +{ + char *ptr = strchr(buffer, '\n'); + assert(blen != 0); + if (ptr || (size_t)(current-buffer) >= blen-1) + return 1; + return 0; +} + +int remote_fgets(char *buf, size_t blen, int fd) +{ + int complete = 0; + size_t line_len; + char *line_end = NULL; + + assert(blen != 0); + /* See if we have more in the buffer first */ + if (current != buffer) { + line_end = strchr(buffer, '\n'); + if (line_end == NULL && (size_t)(current - buffer) >= blen-1) + line_end = current-1; // have enough to fill blen, so point to end + } + + /* Otherwise get some new bytes */ + if (line_end == NULL && current != eptr && !eof) { + ssize_t len; + + /* Use current since we may be adding more */ + do { + len = read(fd, current, eptr - current); + } while (len < 0 && errno == EINTR); + if (len < 0) + return -1; + if (len == 0) + eof = 1; + else + current[len] = 0; + current += len; + + /* Start from beginning to see if we have one */ + line_end = strchr(buffer, '\n'); + } + + /* See what we have */ + if (line_end) { + /* Include the last character (usually newline) */ + line_len = (line_end+1) - buffer; + /* Make sure we are within the right size */ + if (line_len > blen-1) + line_len = blen-1; + complete = 1; + } else if (current == eptr) { + /* We are full but no newline */ + line_len = blen-1; + complete = 1; + } else if (current >= buffer+blen-1) { + /* Not completely full, no newline, but enough to fill buf */ + line_len = blen-1; + complete = 1; + } + if (complete) { + size_t remainder_len; + + /* Move to external buf and terminate it */ + memcpy(buf, buffer, line_len); + buf[line_len] = 0; + remainder_len = current - (buffer + line_len); + if (remainder_len > 0) { + /* We have a few leftover bytes to move */ + memmove(buffer, buffer+line_len, remainder_len); + current = buffer+remainder_len; + } else { + /* Got the whole thing, just reset */ + current = buffer; + } + *current = 0; + } + return complete; +} diff --git a/framework/src/audit/audisp/plugins/remote/remote-fgets.h b/framework/src/audit/audisp/plugins/remote/remote-fgets.h new file mode 100644 index 00000000..cb6b2d51 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/remote-fgets.h @@ -0,0 +1,33 @@ +/* remote-fgtes.h -- a replacement for glibc's fgets + * Copyright 2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#ifndef REMOTE_FGETS_HEADER +#define REMOTE_FGETS_HEADER + +#include + +int remote_fgets_eof(void); +int remote_fgets_more(size_t blen); +int remote_fgets(char *buf, size_t blen, int fd); + +#endif + diff --git a/framework/src/audit/audisp/plugins/remote/test-queue.c b/framework/src/audit/audisp/plugins/remote/test-queue.c new file mode 100644 index 00000000..cbf815e8 --- /dev/null +++ b/framework/src/audit/audisp/plugins/remote/test-queue.c @@ -0,0 +1,367 @@ +/* test-queue.c -- test suite for persistent-queue.c + * Copyright 2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Miloslav Trmač + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "queue.h" + +#define NUM_ENTRIES 7 +/* 3*4096, larger than MAX_AUDIT_MESSAGE_LENGTH. The same value is used in the + main audisp-remote code. */ +#define ENTRY_SIZE 12288 + +static char filename[] = "/tmp/tqXXXXXX"; +static struct queue *q; + +static char *sample_entries[NUM_ENTRIES - 1]; +#define NUM_SAMPLE_ENTRIES (sizeof(sample_entries) / sizeof(*sample_entries)) + +#define die(...) die__(__LINE__, __VA_ARGS__) +static void __attribute__((format (printf, 2, 3))) +die__(int line, const char *message, ...) +{ + va_list ap; + + fprintf(stderr, "test-queue: %d: ", line); + va_start(ap, message); + vfprintf(stderr, message, ap); + va_end(ap); + putc('\n', stderr); + abort(); +} + +#define err(...) err__(__LINE__, __VA_ARGS__) +static void __attribute__((format (printf, 2, 3))) +err__(int line, const char *message, ...) +{ + char *errno_str; + va_list ap; + + errno_str = strerror(errno); + fprintf(stderr, "test-queue: %d: ", line); + va_start(ap, message); + vfprintf(stderr, message, ap); + va_end(ap); + fprintf(stderr, ": %s\n", errno_str); + abort(); +} + +static void +init_sample_entries(void) +{ + size_t i; + + for (i = 0; i < NUM_SAMPLE_ENTRIES; i++) { + char *e; + size_t j, len; + + len = rand() % ENTRY_SIZE; + e = malloc(len + 1); + if (e == NULL) + err("malloc"); + for (j = 0; j < len; j++) + e[j] = rand() % CHAR_MAX + 1; + e[j] = '\0'; + sample_entries[i] = e; + } +} + +static void +free_sample_entries(void) +{ + size_t i; + + for (i = 0; i < NUM_SAMPLE_ENTRIES; i++) + free(sample_entries[i]); +} + +static void +test_q_open(void) +{ + struct queue *q2; + + /* Test that flags are honored */ + q2 = q_open(Q_IN_FILE | Q_CREAT | Q_EXCL, filename, NUM_ENTRIES, + ENTRY_SIZE); + if (q2 != NULL) + die("q_open didn't fail"); + if (errno != EEXIST) + err("q_open"); + + /* Test that locking is enforced. Use a separate process because + fcntl()/lockf() locking is attached to processes, not file + descriptors. */ + fflush(NULL); + switch (fork()) { + case -1: + err("fork"); + case 0: + q2 = q_open(Q_IN_FILE, filename, NUM_ENTRIES, ENTRY_SIZE); + if (q2 != NULL) + die("q_open didn't fail"); + if (errno != EBUSY) + err("q_open"); + _exit(0); + default: { + int status; + + if (wait(&status) == (pid_t)-1) + err("wait"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + die("wait status %d", status); + } + } +} + +static void +test_empty_q (void) +{ + char buf[ENTRY_SIZE]; + + if (q_peek(q, buf, sizeof(buf)) != 0) + die("q_peek reports non-empty"); + + if (q_drop_head(q) != -1) + die("q_drop_head didn't fail"); + if (errno != EINVAL) + err("q_drop_head"); + + if (q_queue_length(q) != 0) + die("Unexpected q_queue_length"); +} + +static void +test_basic_data (void) +{ + char buf[ENTRY_SIZE + 1]; + int i; + + if (q_append(q, " ") != 0) + die("q_append"); + + memset (buf, 'A', ENTRY_SIZE); + buf[ENTRY_SIZE] = '\0'; + if (q_append(q, buf) != -1) + die("q_append didn't fail"); + if (errno != EINVAL) + err("q_append"); + + buf[ENTRY_SIZE - 1] = '\0'; + if (q_append(q, buf) != 0) + die("q_append"); + + if (q_queue_length(q) != 2) + die("Unexpected q_queue_length"); + + if (q_peek(q, buf, sizeof(buf)) < 1) + err("q_peek"); + if (strcmp(buf, " ") != 0) + die("invalid data returned"); + if (q_drop_head(q) != 0) + err("q_drop_head"); + + if (q_peek(q, buf, ENTRY_SIZE - 1) != -1) + err("q_peek didn't fail"); + if (errno != ERANGE) + err("q_peek"); + for (i = 0; i < 2; i++) { + size_t j; + + if (q_peek(q, buf, sizeof(buf)) < 1) + err("q_peek"); + for (j = 0; j < ENTRY_SIZE - 1; j++) { + if (buf[j] != 'A') + die("invalid data at %zu", j); + } + if (buf[j] != '\0') + die("invalid data at %zu", j); + } + if (q_drop_head(q) != 0) + err("q_drop_head"); + + if (q_queue_length(q) != 0) + die("Unexpected q_queue_length"); +} + +static void +append_sample_entries(size_t count) +{ + size_t i; + + for (i = 0; i < count; i++) { + if (q_append(q, sample_entries[i % NUM_SAMPLE_ENTRIES]) != 0) + die("q_append %zu", i); + } +} + +static void +verify_sample_entries(size_t count) +{ + char buf[ENTRY_SIZE + 1]; + size_t i; + + if (q_queue_length(q) != count) + die("Unexpected q_queue_length"); + for (i = 0; i < count; i++) { + if (q_peek(q, buf, sizeof(buf)) < 1) + err("q_peek %zu", i); + if (strcmp(buf, sample_entries[i % NUM_SAMPLE_ENTRIES]) != 0) + die("invalid data %zu", i); + if (q_drop_head(q) != 0) + err("q_drop_head"); + } + if (q_peek(q, buf, sizeof(buf)) != 0) + die("q_peek reports non-empty"); +} + +static void +test_run(int flags) +{ + size_t j; + + q = q_open(flags | Q_CREAT | Q_EXCL, filename, NUM_ENTRIES, ENTRY_SIZE); + if (q == NULL) + err("q_open"); + + if ((flags & Q_IN_FILE) != 0) + test_q_open(); + + /* Do this enough times to get a wraparound */ + for (j = 0; j < NUM_ENTRIES; j++) { + test_empty_q(); + test_basic_data(); + } + + append_sample_entries(NUM_ENTRIES - 1); + if (q_queue_length(q) != NUM_ENTRIES - 1) + die("Unexpected q_queue_length"); + + q_close(q); + + q = q_open(flags, filename, NUM_ENTRIES, ENTRY_SIZE); + if (q == NULL) + err("q_open"); + if ((flags & Q_IN_FILE) != 0) + /* Test that the queue can be reopened and data has been + preserved. */ + verify_sample_entries(NUM_ENTRIES - 1); + else + /* Test that a new in-memory queue is empty. */ + verify_sample_entries(0); + q_close(q); + + if ((flags & Q_IN_FILE) != 0 && unlink(filename) != 0) + err("unlink"); +} + +static void +test_resizing(void) +{ + q = q_open(Q_IN_FILE | Q_CREAT | Q_EXCL, filename, NUM_ENTRIES, + ENTRY_SIZE); + if (q == NULL) + err("q_open"); + + append_sample_entries(NUM_ENTRIES); + if (q_queue_length(q) != NUM_ENTRIES) + die("Unexpected q_queue_length"); + + q_close(q); + + /* Verify num_entries is validated */ + q = q_open(Q_IN_FILE, filename, NUM_ENTRIES + 1, ENTRY_SIZE); + if (q != NULL) + die("q_open didn't fail"); + if (errno != EINVAL) + err("q_open"); + q = q_open(Q_IN_FILE, filename, NUM_ENTRIES - 1, ENTRY_SIZE); + if (q != NULL) + die("q_open didn't fail"); + if (errno != EINVAL) + err("q_open"); + + /* Test increasing size */ + q = q_open(Q_IN_FILE | Q_RESIZE, filename, 2 * NUM_ENTRIES, ENTRY_SIZE); + if (q == NULL) + err("q_open"); + verify_sample_entries(NUM_ENTRIES); + + append_sample_entries(NUM_ENTRIES); + q_close(q); + + /* Test decreasing size */ + q = q_open(Q_IN_FILE | Q_RESIZE, filename, NUM_ENTRIES / 2, ENTRY_SIZE); + if (q != NULL) + die("q_open didn't fail"); + if (errno != ENOSPC) + err("q_open"); + q = q_open(Q_IN_FILE | Q_RESIZE, filename, NUM_ENTRIES, ENTRY_SIZE); + if (q == NULL) + err("q_open"); + verify_sample_entries(NUM_ENTRIES); + q_close(q); + + if (unlink(filename) != 0) + err("unlink"); +} + +int +main(void) +{ + static const int flags[] = { + Q_IN_MEMORY, + Q_IN_FILE, + Q_IN_FILE | Q_SYNC, + Q_IN_MEMORY | Q_IN_FILE + }; + + int fd; + size_t i; + + init_sample_entries(); + + /* We really want tmpnam() here (safe due to the Q_EXCL below), but + gcc warns on any use of tmpnam(). */ + fd = mkstemp(filename); + if (fd == -1) + err("tmpnam"); + if (close(fd) != 0) + err("close"); + if (unlink(filename) != 0) + err("unlink"); + + for (i = 0; i < sizeof(flags) / sizeof(*flags); i++) + test_run(flags[i]); + + test_resizing(); + + free_sample_entries(); + + return EXIT_SUCCESS; +} diff --git a/framework/src/audit/audisp/plugins/zos-remote/Makefile.am b/framework/src/audit/audisp/plugins/zos-remote/Makefile.am new file mode 100644 index 00000000..ac83a74d --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/Makefile.am @@ -0,0 +1,52 @@ +# Makefile.am-- +# Copyright (C) 2007,2008 International Business Machines Corp. +# Copyright (C) 2011, 2015 Red Hat., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Klaus Heinrich Kiwi +# + +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib -I${top_srcdir}/auparse +CONFIG_CLEAN_FILES = *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +EXTRA_DIST = zos-remote.conf audispd-zos-remote.conf +LIBS = -L${top_builddir}/auparse -lauparse +LDADD = -lpthread -lldap -llber $(CAPNG_LDADD) +dispatcher_confdir = $(sysconfdir)/audisp +plugin_confdir=$(dispatcher_confdir)/plugins.d +plugin_conf = zos-remote.conf +dispatcher_conf = audispd-zos-remote.conf +sbin_PROGRAMS = audispd-zos-remote + +noinst_HEADERS = zos-remote-log.h zos-remote-ldap.h zos-remote-config.h \ + zos-remote-queue.h +audispd_zos_remote_SOURCES = zos-remote-plugin.c zos-remote-log.c \ + zos-remote-ldap.c zos-remote-config.c zos-remote-queue.c +audispd_zos_remote_CFLAGS = -W -Wall -Wundef -D_GNU_SOURCE -fPIE -DPIE +audispd_zos_remote_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now + +install-data-hook: + mkdir -p -m 0750 ${DESTDIR}${plugin_confdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/$(plugin_conf) \ + ${DESTDIR}${dispatcher_confdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/$(dispatcher_conf) \ + ${DESTDIR}${plugin_confdir} + +uninstall-hook: + rm ${DESTDIR}${plugin_confdir}/$(dispatcher_conf) + rm ${DESTDIR}${dispatcher_confdir}/$(plugin_conf) diff --git a/framework/src/audit/audisp/plugins/zos-remote/audispd-zos-remote.conf b/framework/src/audit/audisp/plugins/zos-remote/audispd-zos-remote.conf new file mode 100644 index 00000000..13aef2ce --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/audispd-zos-remote.conf @@ -0,0 +1,14 @@ +# This is the configuration for the audispd-zos-remote +# audit dispatcher plugin - See audispd(8) +# +# Note that this specific plugin has a configuration file of +# its own. The complete path for this file must be entered as +# the argument for the plugin in the 'args' field below +# See audispd-zos-remote(8) + +active = no +direction = out +path = /sbin/audispd-zos-remote +type = always +args = /etc/audisp/zos-remote.conf +format = string diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.c new file mode 100644 index 00000000..b92dc778 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.c @@ -0,0 +1,443 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + * based on code by Steve Grubb * + ***************************************************************************/ + +#include "zos-remote-config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "zos-remote-log.h" + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; + const char *option; +}; + +struct kw_pair +{ + const char *name; + int (*parser) (struct nv_pair *, int, plugin_conf_t *); + int max_options; +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *, char *); +static int nv_split(char *, struct nv_pair *); +static const struct kw_pair *kw_lookup(const char *); +static int server_parser(struct nv_pair *, int, plugin_conf_t *); +static int port_parser(struct nv_pair *, int, plugin_conf_t *); +static int timeout_parser(struct nv_pair *, int, plugin_conf_t *); +static int user_parser(struct nv_pair *, int, plugin_conf_t *); +static int password_parser(struct nv_pair *, int, plugin_conf_t *); +static int q_depth_parser(struct nv_pair *, int, plugin_conf_t *); +static int sanity_check(plugin_conf_t *); + +static const struct kw_pair keywords[] = { + {"server", server_parser, 0}, + {"port", port_parser, 0}, + {"timeout", timeout_parser, 0}, + {"user", user_parser, 0}, + {"password", password_parser, 0}, + {"q_depth", q_depth_parser, 0}, + {NULL, NULL, 0} +}; + +#define UNUSED(x) (void)(x) + +/* + * Set everything to its default value +*/ +void plugin_clear_config(plugin_conf_t * c) +{ + c->server = NULL; + c->port = 0; + c->user = NULL; + c->password = NULL; + c->timeout = 15; + c->q_depth = 64; + /* not re-setting counter */ +} + +int plugin_load_config(plugin_conf_t * c, const char *file) +{ + int fd, rc, mode, lineno = 1; + struct stat st; + FILE *f; + char buf[128]; + + plugin_clear_config(c); + + /* open the file */ + mode = O_RDONLY; + rc = open(file, mode); + if (rc < 0) { + if (errno != ENOENT) { + log_err("Error opening %s (%s)", file, + strerror(errno)); + return 1; + } + log_warn("Config file %s doesn't exist, skipping", file); + return 1; + } + fd = rc; + + /* check the file's permissions: owned by root, not world anything, + * not symlink. + */ + if (fstat(fd, &st) < 0) { + log_err("Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + log_err("Error - %s isn't owned by root", file); + close(fd); + return 1; + } + if ((st.st_mode & (S_IRUSR | S_IWUSR | S_IRGRP)) != + (S_IRUSR | S_IWUSR | S_IRGRP)) { + log_err("%s permissions should be 0640", file); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + log_err("Error - %s is not a regular file", file); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "r"); + if (f == NULL) { + log_err("Error - fdopen failed (%s)", strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf)) { + /* convert line into name-value pair */ + const struct kw_pair *kw; + struct nv_pair nv; + + rc = nv_split(buf, &nv); + switch (rc) { + case 0: /* fine */ + break; + case 1: /* not the right number of tokens. */ + log_err("Wrong number of arguments for line %d in %s", lineno, file); + break; + case 2: /* no '=' sign */ + log_err("Missing equal sign for line %d in %s", + lineno, file); + break; + default: /* something else went wrong... */ + log_err("Unknown error for line %d in %s", + lineno, file); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + log_err("Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, file); + fclose(f); + return 1; + } + + /* Check number of options */ + if (kw->max_options == 0 && nv.option != NULL) { + log_err("Keyword \"%s\" has invalid option " + "\"%s\" in line %d of %s", + nv.name, nv.option, lineno, file); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(&nv, lineno, c); + if (rc != 0) { + fclose(f); + return 1; /* local parser puts message out */ + } + + lineno++; + } + + fclose(f); + c->name = strdup(basename(file)); + if (lineno > 1) + return sanity_check(c); + return 0; +} + +static char *get_line(FILE * f, char *buf) +{ + if (fgets_unlocked(buf, 128, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + + if (ptr) + *ptr = 0; + return buf; + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr, *saved; + + nv->name = NULL; + nv->value = NULL; + nv->option = NULL; + ptr = strtok_r(buf, " ", &saved); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* See if there's an option */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) { + nv->option = ptr; + + /* Make sure there's nothing else */ + ptr = strtok_r(NULL, " ", &saved); + if (ptr) + return 1; + } + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + + +static int server_parser(struct nv_pair *nv, int line, plugin_conf_t * c) +{ + UNUSED(line); + if (nv->value == NULL) + c->server = NULL; + else + c->server = strdup(nv->value); + + return 0; +} + +static int port_parser(struct nv_pair *nv, int line, plugin_conf_t * c) +{ + const char *ptr = nv->value; + unsigned long i; + + /* check that all chars are numbers */ + for (i = 0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + log_err("Value %s should only be numbers - line %d", nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + log_err("Error converting string to a number (%s) - line %d", strerror(errno), line); + return 1; + } + + c->port = i; + return 0; + +} + +static int timeout_parser(struct nv_pair *nv, int line, plugin_conf_t * c) +{ + const char *ptr = nv->value; + unsigned long i; + + /* check that all chars are numbers */ + for (i = 0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + log_err("Value %s should only be numbers - line %d", nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + log_err("Error converting string to a number (%s) - line %d", strerror(errno), line); + return 1; + } + + c->timeout = i; + return 0; + +} + + +static int user_parser(struct nv_pair *nv, int line, plugin_conf_t * c) +{ + UNUSED(line); + if (nv->value == NULL) + c->user = NULL; + else + c->user = strdup(nv->value); + + return 0; +} + +static int password_parser(struct nv_pair *nv, int line, plugin_conf_t * c) +{ + UNUSED(line); + if (nv->value == NULL) + c->password = NULL; + else + c->password = strdup(nv->value); + + return 0; +} + +static int q_depth_parser(struct nv_pair *nv, int line, plugin_conf_t * c) +{ + const char *ptr = nv->value; + unsigned long i; + + /* check that all chars are numbers */ + for (i = 0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + log_err("Value %s should only be numbers - line %d", nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + log_err("Error converting string to a number (%s) - line %d", strerror(errno), line); + return 1; + } + + if (i < 16 || i > 99999) { + log_err("q_depth must be between 16 and 99999"); + return 1; + } + + c->q_depth = i; + return 0; + +} + + +/* + * Check configuration.At this point, all fields have been read. + * Returns 0 if no problems and 1 if problems detected. + */ +static int sanity_check(plugin_conf_t * c) +{ + /* Error checking */ + if (!c->server) { + log_err("Error - no server hostname given"); + return 1; + } + + if (!c->user) { + log_err("Error - no bind user given"); + return 1; + } + + if (!c->password) { + log_err("Error - no password given"); + return 1; + } + + if (!c->timeout) { + log_err("Error - timeout can't be zero"); + return 1; + } + return 0; +} + +void plugin_free_config(plugin_conf_t * c) +{ + + if (c == NULL) + return; + + free((void *) c->server); + free((void *) c->user); + free((void *) c->password); + free((void *) c->name); +} diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.h b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.h new file mode 100644 index 00000000..82bf365f --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-config.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + * based on code by Steve Grubb * + ***************************************************************************/ + +#ifndef _ZOS_REMOTE_CONFIG_H +#define _ZOS_REMOTE_CONFIG_H + + +/*************************************************************************** + * z/OS Remote-services Plugin configuration * + ***************************************************************************/ +typedef struct plugin_conf +{ + char *name; + char *server; + unsigned int port; + char *user; + char *password; + long timeout; + unsigned int q_depth; + unsigned int counter; +} plugin_conf_t; + +void plugin_clear_config(plugin_conf_t *); +int plugin_load_config(plugin_conf_t *, const char *); +void plugin_free_config(plugin_conf_t *); + +#endif /* _ZOS_REMOTE_CONFIG_H */ diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c new file mode 100644 index 00000000..209743f3 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.c @@ -0,0 +1,608 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + ***************************************************************************/ + +#include "zos-remote-ldap.h" + +#include +#include +#include +#include +#include "zos-remote-log.h" + +/*************************************************************************** + * Audit response struct * + ***************************************************************************/ +typedef struct audit_resp_item +{ + ber_int_t version; /* Version of Response data itself */ + ber_int_t itemTag; /* Copy of itemTag from Operation */ + ber_int_t majorCode; /* Majorcode. Main return code of this Outcome */ + ber_int_t minorCode1; /* minorCode1. SAFRc or other Rc */ + ber_int_t minorCode2; /* minorCode2. RacfRc or other Rc */ + ber_int_t minorCode3; /* minorCode3. RacfRsn or other Rc */ +} audit_resp_item_t; + +typedef struct audit_response +{ + ber_int_t respVersion; /* Overall version */ + ber_int_t respMajor; /* Overall major code */ + unsigned int numItems; /* Number of response items */ + audit_resp_item_t **itemList; /* response ItemList */ +} audit_response_t; + + +/*************************************************************************** + * z/OS Remote-services Major return code handling * + ***************************************************************************/ +struct zos_remote_error +{ + int code; + char *str; +}; + +static struct zos_remote_error zos_remote_errlist[] = { + {ZOS_REMOTE_MAJOR_SUCCESS, "Success"}, + {ZOS_REMOTE_MAJOR_WARNINGMODE, "WARNINGMODE - Event was logged, with warnings"}, + {ZOS_REMOTE_MAJOR_NOTREQ, "NOTREQ - No logging required"}, + {ZOS_REMOTE_MAJOR_UNDETERMINED, "UNDETERMINED - Undetermined result"}, + {ZOS_REMOTE_MAJOR_UNAUTHORIZED, "UNAUTHORIZED - The user does not have authority the R_auditx service"}, + {ZOS_REMOTE_MAJOR_RACROUTE, "RACROUTE - The R_auditx service returned an unexpected error"}, + {ZOS_REMOTE_MAJOR_VAL_ERR, "VAL_ERR - Value error in request"}, + {ZOS_REMOTE_MAJOR_ENC_ERR, "ENC_ERR - DER decoding error in request"}, + {ZOS_REMOTE_MAJOR_UNSUF_AUTH, "UNSUF_AUTH - The user has unsuficient authority for the requested function"}, + {ZOS_REMOTE_MAJOR_EMPTY, "EMPTY - Empty request received - No items found within the ItemList"}, + {ZOS_REMOTE_MAJOR_INVALID_VER, "INVALID_VER - Invalid RequestVersion"}, + {ZOS_REMOTE_MAJOR_INTERNAL_ERR, "INTERNAL_ERR - An internal error was encountered within the ICTX component"}, + {-1, NULL} +}; + +/*************************************************************************** + * Internal functions prototypes * + ***************************************************************************/ +static int _zos_remote_init(ZOS_REMOTE *); +static void _zos_remote_destroy(ZOS_REMOTE *); +static int zos_remote_connect(ZOS_REMOTE *); +static void zos_remote_disconnect(ZOS_REMOTE *); +static int submit_xop_s(ZOS_REMOTE *, struct berval *); +static int decode_response(audit_response_t *, struct berval *); + +/*************************************************************************** + * Exported functions * + ***************************************************************************/ +int submit_request_s(ZOS_REMOTE *zos_remote, BerElement *ber) +{ + int rc, retry = 1; /* retry once and give up */ + struct berval bv; + + rc = ber_flatten2(ber, &bv, 0); /* 0 = Use ber's buffer */ + if (rc == -1) { + log_err("Error flattening BER element"); + return ICTX_E_ABORT; + } + +retry: + rc = submit_xop_s(zos_remote, &bv); + switch (rc) { + case ICTX_SUCCESS: + break; + case ICTX_E_TRYAGAIN: + /* + * Usually means that the server connection timed-out + * So we flush the LDAP connection by unsetting the + * 'connected' flag and trying again. + */ + if (retry > 0) { + log_debug("Connection seems down - retrying"); + retry--; + _zos_remote_destroy(zos_remote); + rc = _zos_remote_init(zos_remote); + if (rc != ICTX_SUCCESS) + log_err("Error - failed to re-initialize LDAP session"); + else + goto retry; /* go to submit_xop_s once more */ + } + log_err("Can't establish connection"); + break; + case ICTX_E_ABORT: + break; + default: + log_err("Event resulted failure, code: 0x%x", rc); + } + + return rc; +} + +int zos_remote_init(ZOS_REMOTE *zos_remote, const char *server, int port, + const char *user, const char *password, int timeout) +{ + zos_remote->server = strdup(server); + zos_remote->port = port; + zos_remote->user = strdup(user); + zos_remote->password = strdup(password); + zos_remote->timeout = timeout; + zos_remote->connected = 0; + + if (!zos_remote->server || !zos_remote->user || !zos_remote->password) { + log_err("Error allocating memory for session members"); + return ICTX_E_FATAL; + } + + return _zos_remote_init(zos_remote); +} + +void zos_remote_destroy(ZOS_REMOTE *zos_remote) +{ + _zos_remote_destroy(zos_remote); + + free(zos_remote->server); + free(zos_remote->user); + free(zos_remote->password); +} + +char *zos_remote_err2string(int err) +{ + int i; + + for (i = 0; zos_remote_errlist[i].str != NULL; i++) { + if (err == zos_remote_errlist[i].code) + return zos_remote_errlist[i].str; + } + return "Unknown error"; +} + +/*************************************************************************** + * Internal Functions * + ***************************************************************************/ +static int _zos_remote_init(ZOS_REMOTE *zos_remote) +{ + int version, rc; + char *uri = NULL; + +#ifdef LDAP_DEPRECATED + + log_debug("Initializing z/OS Remote-services LDAP connection at ldap://%s:%d", + zos_remote->server, zos_remote->port); + zos_remote->ld = ldap_init(zos_remote->server + zos_remote->port ? zos_remote->port : LDAP_PORT); + if (zos_remote->ld == NULL) { + log_err("Error initializing LDAP session: %s", + strerror(errno)); + rc = ICTX_E_FATAL; + goto end; + } +#else + /* build ldap URI */ + if (zos_remote->port == 0 || zos_remote->port == LDAP_PORT) + rc = asprintf(&uri, "ldap://%s", zos_remote->server); + else + rc = asprintf(&uri, "ldap://%s:%d", zos_remote->server, + zos_remote->port); + + if (rc == -1) { + log_err("Out of memory building LDAP server URI"); + rc = ICTX_E_FATAL; + uri = NULL; + goto end; + } + + log_debug("Initializing z/OS Remote-services LDAP connection at %s", uri); + /* Get a handle to an LDAP connection */ + rc = ldap_initialize(&zos_remote->ld, uri); + if (rc != LDAP_SUCCESS) { + log_err("Error initializing LDAP session: %s", + ldap_err2string(rc)); + rc = ICTX_E_FATAL; + goto free_uri; + } +#endif + + /* + * Ensure the LDAP protocol version supported by the client + * to 3. (Extended operations are part of version 3). + */ + rc = ldap_get_option(zos_remote->ld, LDAP_OPT_PROTOCOL_VERSION, + &version); + if (rc != LDAP_OPT_SUCCESS) { + log_err("Error getting LDAP session options"); + rc = ICTX_E_FATAL; + goto unbind; + } + + if (version < LDAP_VERSION3) { + log_debug("Setting LDAP session version to %d", + LDAP_VERSION3); + version = LDAP_VERSION3; + rc = ldap_set_option(zos_remote->ld, LDAP_OPT_PROTOCOL_VERSION, + &version); + if (rc != LDAP_OPT_SUCCESS) { + log_err("Error setting LDAP session version"); + rc = ICTX_E_FATAL; + goto unbind; + } + } + + goto free_uri; + +unbind: + ldap_unbind_ext_s(zos_remote->ld, NULL, NULL); + zos_remote->ld = NULL; + +free_uri: + free(uri); + +end: + return rc; +} + +static void _zos_remote_destroy(ZOS_REMOTE *zos_remote) +{ + zos_remote_disconnect(zos_remote); + zos_remote->ld = NULL; +} + +static int zos_remote_connect(ZOS_REMOTE *zos_remote) +{ + struct berval cred; + int rc; + char bindusr[255]; + + snprintf(bindusr, 255, "racfid=%s,cn=ictx", zos_remote->user); + + log_debug("Attempting BIND. User '%s', password ''", + bindusr); + + cred.bv_val = (char *) zos_remote->password; + cred.bv_len = strlen(zos_remote->password); + + rc = ldap_sasl_bind_s(zos_remote->ld, bindusr, + LDAP_SASL_SIMPLE, &cred, + NULL, NULL, NULL); + + + switch (rc) { + case LDAP_SUCCESS: + log_debug("LDAP BIND succeeded"); + zos_remote->connected = 1; + rc = ICTX_SUCCESS; + break; + case LDAP_SERVER_DOWN: + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + case LDAP_TIMEOUT: + case LDAP_CONNECT_ERROR: + log_warn("z/OS Remote-services connection failed: %s", + ldap_err2string(rc)); + rc = ICTX_E_TRYAGAIN; + break; + default: + log_err("Error - z/OS Remote-services initialization failed: %s", + ldap_err2string(rc)); + rc = ICTX_E_FATAL; + } + + return rc; +} + + +static void zos_remote_disconnect(ZOS_REMOTE *zos_remote) +{ + if (zos_remote->ld) { + log_debug("Unbinding LDAP session"); + +#ifdef LDAP_DEPRECATED + ldap_unbind(zos_remote->ld); +#else + ldap_unbind_ext_s(zos_remote->ld, NULL, NULL); +#endif + } + zos_remote->connected = 0; + +} + +/* + * Sync-submit extended operation given in *bv + * return ICTX_SUCCESS if submission (and response) + * succeeded. + * Log errors using log_err() functions + */ +int submit_xop_s(ZOS_REMOTE *zos_remote, struct berval *bv) +{ + LDAPMessage *result; + audit_response_t response; + int rc, errcode, msgId; + unsigned int i; + char *errmsg, *oid; + struct berval *bv_response; + struct timeval t; + + if (zos_remote->connected == 0) { + rc = zos_remote_connect(zos_remote); + if (rc != ICTX_SUCCESS) + return rc; + } + + /* call LDAP - won't block */ + rc = ldap_extended_operation(zos_remote->ld, ICTX_OIDAUDITREQUEST, + bv, NULL, NULL, &msgId); + if (rc == LDAP_SERVER_DOWN) { + zos_remote->connected = 0; + return ICTX_E_TRYAGAIN; + } else if (rc != LDAP_SUCCESS) { + log_err("LDAP extended operation submission failure: %s", + ldap_err2string(rc)); + return ICTX_E_ABORT; + } else { + log_debug("Sent LDAP extended operation request, msgId=0x%x", + msgId); + } + + /* call blocking ldap_result with specified timeout */ + t.tv_sec = zos_remote->timeout; + t.tv_usec = 0; + rc = ldap_result(zos_remote->ld, msgId, 1, &t, &result); + + if (rc == -1) { + /* error in ldap operation */ + ldap_get_option(zos_remote->ld, LDAP_OPT_ERROR_NUMBER, &errcode); + switch (errcode) { + case LDAP_SERVER_DOWN: + /* Connection may have timed out, let's retry */ + zos_remote->connected = 0; + rc = ICTX_E_TRYAGAIN; + break; + default: + log_err("ldap_result unexpected failure: %s (0x%x)", + ldap_err2string(rc), rc); + rc = ICTX_E_ABORT; + } + goto end; + } else if (rc == 0) { + /* timeout reached */ + log_warn("LDAP extended operation timed out"); + rc = ICTX_E_ABORT; + goto end; + } else if (rc != LDAP_RES_EXTENDED) { + /* not an extended operation response! */ + log_err("LDAP extended operation resulted in unexpected answer: 0x%x", rc); + rc = ICTX_E_ABORT; + goto free_result; + } + + log_debug("Got LDAP Extended result"); + /* + * we have an extended operation result + * first parse_result will check for errcode, later + * parse_extended_result will give us the oid and the BER value + */ + rc = ldap_parse_result(zos_remote->ld, result, &errcode, NULL, + &errmsg, NULL, NULL, 0); + if (rc != LDAP_SUCCESS) { + log_err("LDAP parse result internal failure (code 0x%x)", + rc); + rc = ICTX_E_ABORT; + goto free_result; + } + + if (errcode != LDAP_SUCCESS) { + log_err("LDAP extended operation failed: %s", errmsg); + rc = ICTX_E_ABORT; + goto free_errmsg; + } + + rc = ldap_parse_extended_result(zos_remote->ld, result, &oid, + &bv_response, 0); + if (rc != LDAP_SUCCESS) { + log_err("Failed to parse ldap extended result (code 0x%x)", + rc); + rc = ICTX_E_ABORT; + goto free_errmsg; + } + + if (oid && strcmp(oid, ICTX_OIDAUDITRESPONSE) != 0) { + /* oid == null shouldn't be a problem to log_err */ + log_err("LDAP extended operation returned an invalid oid: %s", oid); + rc = ICTX_E_ABORT; + goto free_bv; + } + + rc = decode_response(&response, bv_response); + if (rc != ICTX_SUCCESS) { + log_err("Error decoding extended operation response"); + goto free_bv; + } + + if (response.respMajor == ZOS_REMOTE_MAJOR_SUCCESS) { + /* submission was successful, no further processing needed */ + log_debug("Successfully submited Remote audit Request"); + rc = ICTX_SUCCESS; + goto free_response; + } else if (response.respMajor == ZOS_REMOTE_MAJOR_EMPTY) { + /* something is going on. Set error and stop processing */ + log_warn("Warning - LDAP extended operation returned empty result"); + rc = ICTX_E_ABORT; + goto free_response; + } else if (response.respMajor == ZOS_REMOTE_MAJOR_WARNINGMODE || + response.respMajor == ZOS_REMOTE_MAJOR_NOTREQ) + rc = ICTX_SUCCESS; /* don't fail, but continue processing */ + else + rc = ICTX_E_ABORT; /* set return code and continue processing */ + + /* If it's not success nor empty, let's check for errors in the response */ + for (i = 0; i < response.numItems; i++) { + switch ((response.itemList[i])->majorCode) { + /* 0 <= Major Code <= 14 */ + case ZOS_REMOTE_MAJOR_SUCCESS: + break; + case ZOS_REMOTE_MAJOR_WARNINGMODE: + case ZOS_REMOTE_MAJOR_NOTREQ: + log_debug("Warning - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + log_debug("SAF code: 0x%x, RACF code: 0x%x, RACF reason: 0x%x", + (response.itemList[i])->minorCode1, + (response.itemList[i])->minorCode2, + (response.itemList[i])->minorCode3); + break; + case ZOS_REMOTE_MAJOR_UNDETERMINED: + case ZOS_REMOTE_MAJOR_UNAUTHORIZED: + case ZOS_REMOTE_MAJOR_RACROUTE: + log_err("Error - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + log_err("SAF code: 0x%x, RACF code: 0x%x, RACF reason: 0x%x", + (response.itemList[i])->minorCode1, + (response.itemList[i])->minorCode2, + (response.itemList[i])->minorCode3); + break; + /* 16 <= Major Code <= 20 */ + case ZOS_REMOTE_MAJOR_VAL_ERR: + case ZOS_REMOTE_MAJOR_ENC_ERR: + log_err("Error - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + log_err("Item field: %d, reson %d", + (response.itemList[i])-> + minorCode1, + (response.itemList[i])->minorCode2); + break; + /* 24 <= Major code <= 100 */ + case ZOS_REMOTE_MAJOR_UNSUF_AUTH: + case ZOS_REMOTE_MAJOR_EMPTY: + case ZOS_REMOTE_MAJOR_INVALID_VER: + case ZOS_REMOTE_MAJOR_INTERNAL_ERR: + log_err("Error - LDAP extended operation returned '%s' for item %d", + zos_remote_err2string((response.itemList[i])->majorCode), + (response.itemList[i])->itemTag); + break; + default: + log_err("Error - LDAP extended operation returned an unknown Major code for item %d", + (response.itemList[i])->majorCode); + } + } + +free_response: + for (; response.numItems > 0; response.numItems--) + free(response.itemList[response.numItems - 1]); + free(response.itemList); + +free_bv: + if (bv_response) + ber_bvfree(bv_response); + if (oid) + ldap_memfree(oid); + +free_errmsg: + ldap_memfree(errmsg); + +free_result: + ldap_msgfree(result); + +end: + return rc; +} + +static int decode_response(audit_response_t * r, struct berval *bv) +{ + BerElement *ber; + ber_len_t len; + int rc; + + if (!bv) { + log_err("LDAP extended operation returned NULL message"); + return ICTX_E_ABORT; + } else if ((ber = ber_init(bv)) == NULL) { + log_err("Error initializing BER response data"); + return ICTX_E_ABORT; + } + + log_debug("---Got an encoded request response:"); + debug_bv(bv); + + r->respVersion = 0; + r->respMajor = 0; + r->numItems = 0; + r->itemList = NULL; + + rc = ber_scanf(ber, "{ii", &r->respVersion, &r->respMajor); + if (r->respVersion != ICTX_REQUESTVER) { + log_err("Invalid version returned by z/OS Remote-services server"); + log_err("Should be %d, got %d", ICTX_REQUESTVER, + r->respVersion); + rc = ICTX_E_ABORT; + goto free_ber; + } + + if (r->respMajor == ZOS_REMOTE_MAJOR_SUCCESS || + r->respMajor == ZOS_REMOTE_MAJOR_EMPTY) { + rc = ICTX_SUCCESS; + /* No further processing required */ + goto free_ber; + } + + /* Inspect ber response otherwise */ + while (ber_peek_tag(ber, &len) == LBER_SEQUENCE) { + r->numItems++; + r->itemList = (audit_resp_item_t **) realloc(r->itemList, + r->numItems * + sizeof + (audit_resp_item_t + *)); + if (errno == ENOMEM) { + if (r->itemList) + free(r->itemList); + rc = ICTX_E_FATAL; + goto free_ber; + } + + audit_resp_item_t *item = (audit_resp_item_t *) + malloc(sizeof(audit_resp_item_t)); + + if (!item) { + rc = ICTX_E_FATAL; + goto free_ber; + } + + rc |= ber_scanf(ber, "{{iiiiii}}", + &item->version, + &item->itemTag, + &item->majorCode, + &item->minorCode1, &item->minorCode2, + &item->minorCode3); + r->itemList[r->numItems - 1] = item; + } + rc |= ber_scanf(ber, "}"); + + if (rc == -1) { + for (; r->numItems > 0; r->numItems--) + free(r->itemList[r->numItems - 1]); + free(r->itemList); + rc = ICTX_E_ABORT; + } + else + rc = ICTX_SUCCESS; + +free_ber: + ber_free(ber, 1); + + return rc; +} diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.h b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.h new file mode 100644 index 00000000..5767b96e --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-ldap.h @@ -0,0 +1,312 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + ***************************************************************************/ + +#ifndef _ZOS_REMOTE_LDAP_H +#define _ZOS_REMOTE_LDAP_H + +#include +#include + + +/*************************************************************************** + * LDAP Extended Op OID for ICTX Audit * + ***************************************************************************/ +/* ICTX EIM component AUDIT Request OID */ +#define ICTX_OIDAUDITREQUEST "1.3.18.0.2.12.68" + +/* The AUDIT Response OID */ +#define ICTX_OIDAUDITRESPONSE "1.3.18.0.2.12.69" + +/* This implementation version + Request and response must match this */ +#define ICTX_REQUESTVER 0x1 + +/* Needed for BER-encoding */ +#define ASN1_IA5STRING_TAG 0x16 + +/*************************************************************************** + * the ASN.1 struct for the remote audit request and response: * + * * + * RequestValue ::= SEQUENCE { * + * RequestVersion INTEGER, * + * ItemList SEQUENCE OF * + * Item SEQUENCE { * + * ItemVersion INTEGER, * + * ItemTag INTEGER, * + * LinkValue OCTET STRING SIZE(8), * + * Violation BOOLEAN, * + * Event INTEGER, * + * Qualifier INTEGER, * + * Class IA5String, * + * Resource IA5String, * + * LogString IA5String, * + * DatafieldList SEQUENCE OF * + * DataField SEQUENCE { * + * TYPE INTEGER, * + * VALUE IA5STRING * + * } * + * } * + * } * + * * + * Response ::= SEQUENCE { * + * Version INTEGER, * + * ResponseCode INTEGER, * + * ItemList SEQUENCE OF * + * Item SEQUENCE { * + * ItemVersion INTEGER, * + * ItemTag INTEGER, * + * MajorCode INTEGER, * + * MinorCode1 INTEGER, * + * MinorCode2 INTEGER, * + * MinorCode3 INTEGER * + * } * + * } * + ***************************************************************************/ + +/*************************************************************************** + * z/OS Remote-services Audit Minor return codes meaning + +Major Code Meaning +---------- --------------------------------------------------------- +0-14 - MinorCode1 is the SAF return code + - MinorCode2 is the RACF return code + - MinorCode3 is the RACF reason code + +16-20 - MinorCode1 identifies the extended operation request + parameter number (see audit request ASN.1 definition): + 0 - Item + 1 - ItemVersion + 2 - ItemTag + 3 - LinkValue + 4 - Violation + 5 - Event + 6 - Qualifier + 7 - Class + 8 - Resource + 9 - LogString + 10 - DataFieldList + 11 - DataField * + 12 - TYPE * + 13 - VALUE * + - MinorCode2 indicates one of the Following: + 32 - incorrect length + 36 - incorrect value + 40 - encoding error + - MinorCode3 has no defined meaning + +24-100 - MinorCode1 has no defined meaning + - MinorCode2 has no defined meaning + - MinorCode3 has no defined meaning + +* There can be multiple DataField, TYPEs and VALUEs in a request. If any of them is bad + you get the same 11, 12 or 13 MinorCode1. There is no further breakdown of which one + is bad. + + ***************************************************************************/ + +/*************************************************************************** + * Audit Request 'event' field meaning * + ***************************************************************************/ +#define ZOS_REMOTE_EVENT_AUTHENTICATION 0x1 +#define ZOS_REMOTE_EVENT_AUTHORIZATION 0x2 +#define ZOS_REMOTE_EVENT_AUTHORIZATION_MAPPING 0x3 +#define ZOS_REMOTE_EVENT_KEY_MGMT 0x4 +#define ZOS_REMOTE_EVENT_POLICY_MGMT 0x5 +#define ZOS_REMOTE_EVENT_ADMIN_CONFIG 0x6 +#define ZOS_REMOTE_EVENT_ADMIN_ACTION 0x7 + +/*************************************************************************** + * Audit Request 'qualifier' field meaning * + ***************************************************************************/ +#define ZOS_REMOTE_QUALIF_SUCCESS 0x0 +#define ZOS_REMOTE_QUALIF_INFO 0x1 +#define ZOS_REMOTE_QUALIF_WARN 0x2 +#define ZOS_REMOTE_QUALIF_FAIL 0x3 + +/*************************************************************************** + * Relocate types for Audit Request * + ***************************************************************************/ +/* SAF identifier for bind user */ +#define ZOS_REMOTE_RELOC_SAF_BIND_USER 100 + +/* Reguestor's bind user identifier */ +#define ZOS_REMOTE_RELOC_REQ_BIND_USER 101 + +/* Originating security domain */ +#define ZOS_REMOTE_RELOC_ORIG_SECURITY 102 + +/* Originating registry / realm */ +#define ZOS_REMOTE_RELOC_ORIG_REALM 103 + +/* Originating user name */ +#define ZOS_REMOTE_RELOC_ORIG_USER 104 + +/* Mapped security domain */ +#define ZOS_REMOTE_RELOC_MAPPED_SECURITY 105 + +/* Mapped registry / realm */ +#define ZOS_REMOTE_RELOC_MAPPED_REALM 106 + +/* Mapped user name */ +#define ZOS_REMOTE_RELOC_MAPPED_USER 107 + +/* Operation performed */ +#define ZOS_REMOTE_RELOC_OPERATION 108 + +/* Mechanism / object name */ +#define ZOS_REMOTE_RELOC_OBJECT 109 + +/* Method / function used */ +#define ZOS_REMOTE_RELOC_FUNCTION 110 + +/* Key / certificate name */ +#define ZOS_REMOTE_RELOC_CERTIFICATE 111 + +/* Caller subject initiating security event */ +#define ZOS_REMOTE_RELOC_INITIATING_EVENT 112 + +/* Date and time security event occurred */ +#define ZOS_REMOTE_RELOC_TIMESTAMP 113 + +/* Application specific data. (i.e. Other) */ +#define ZOS_REMOTE_RELOC_OTHER 114 + +/*************************************************************************** + * z/OS Remote-services Audit Major return codes * + ***************************************************************************/ +#define ZOS_REMOTE_MAJOR_SUCCESS 0 + +/* Event was logged, with warnings */ +#define ZOS_REMOTE_MAJOR_WARNINGMODE 2 + +/* No logging required + No audit controls are set to require it */ +#define ZOS_REMOTE_MAJOR_NOTREQ 3 + +/* Class not active/ractlisted, + covering profile not found or + RACF is not installed */ +#define ZOS_REMOTE_MAJOR_UNDETERMINED 4 + +/* The user does not have authority the R_auditx service. + The userid associated with the LDAP server must have + at least READ access to the FACILITY class profile IRR.RAUDITX. */ +#define ZOS_REMOTE_MAJOR_UNAUTHORIZED 8 + + +/* The R_auditx service returned an unexpected error. + Compare the returned minor codes with the SAF RACF codes + documented in Security Server Callable Services */ +#define ZOS_REMOTE_MAJOR_RACROUTE 12 + +/* A value specified in the extended operation request is + incorrect or unsupported. Check the returned minor codes + to narrow the reason */ +#define ZOS_REMOTE_MAJOR_VAL_ERR 16 + +/* A DER decoding error was encountered in an item. + Processing Terminated. Partial results may be returned */ +#define ZOS_REMOTE_MAJOR_ENC_ERR 20 + +/* The requestor does not have sufficient authority for the + requested function. The userid associated with the LDAP bind + user must have at least READ access to the FACILITY class + profile IRR.LDAP.REMOTE.AUDIT. */ +#define ZOS_REMOTE_MAJOR_UNSUF_AUTH 24 + +/* No items are found within the ItemList sequence of the extended + operation request, so no response items are returned */ +#define ZOS_REMOTE_MAJOR_EMPTY 28 + +/* Invalid RequestVersion */ +#define ZOS_REMOTE_MAJOR_INVALID_VER 61 + +/* An internal error was encountered within the ICTX component */ +#define ZOS_REMOTE_MAJOR_INTERNAL_ERR 100 + +/*************************************************************************** + * Some standard sizes for remote audit request items * + ***************************************************************************/ +#define ZOS_REMOTE_LINK_VALUE_SIZE 8 +#define ZOS_REMOTE_CLASS_SIZE 8 +#define ZOS_REMOTE_RESOURCE_SIZE 240 +#define ZOS_REMOTE_LOGSTRING_SIZE 200 + + +/*************************************************************************** + * Some standard Error defines * + ***************************************************************************/ +#define ICTX_SUCCESS 0x00 + +/* maybe a temporary failure? */ +#define ICTX_E_TRYAGAIN 0x01 + +/* permanent failure - abort event submission */ +#define ICTX_E_ABORT 0x02 + +/* Fatal failure - abort program */ +#define ICTX_E_FATAL 0x03 + +/* generic error */ +#define ICTX_E_ERROR 0x10 + +/*************************************************************************** + * structure representing an z/OS Remote-services session * + ***************************************************************************/ +typedef struct opaque +{ + char *server; + unsigned int port; + char *user; + char *password; + unsigned int timeout; + LDAP *ld; + int connected; +} ZOS_REMOTE; + +/*************************************************************************** + * LDAP XOP operations * + ***************************************************************************/ +/* + * Initializes z/OS Remote-services (LDAP to ITDS) connection, + * binds to ITDS Server using configured RACF ID + * Args are: + * server, bind user, bind password, server port, timeout + * Caller must call zos_remote_destroy() to free memory allocation + */ +int zos_remote_init(ZOS_REMOTE *, const char *, int, const char *, + const char *, int); + +/* + * Uninitializes z/OS Remote-services (LDAP) connection + */ +void zos_remote_destroy(ZOS_REMOTE *); + +/* + * sync submit request - possibly reconnect to server + * if the connection if found to be dead + */ +int submit_request_s(ZOS_REMOTE *, BerElement *); + + +#endif /* _ZOS_REMOTE_LDAP_H */ diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.c new file mode 100644 index 00000000..a272078e --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.c @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + ***************************************************************************/ +#include "zos-remote-log.h" + +#include +#include +#include +#include "auparse.h" + + +static void vlog_prio(int prio, const char *fmt, va_list ap) +{ + char *str; + + if (asprintf(&str, "pid=%d: %s", mypid, fmt) != -1) { + vsyslog(LOG_DAEMON | prio, str, ap); + free(str); + } +} + +void log_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog_prio(LOG_ERR, fmt, ap); + va_end(ap); +} + +void log_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog_prio(LOG_WARNING, fmt, ap); + va_end(ap); +} + +void log_info(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog_prio(LOG_INFO, fmt, ap); + va_end(ap); +} + +void _log_debug(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog_prio(LOG_INFO, fmt, ap); + va_end(ap); +} + +void _debug_ber(BerElement * ber) +{ + struct berval bv; + + if (ber_flatten2(ber, &bv, 0) != -1) { + debug_bv(&bv); + } +} + +void _debug_bv(struct berval *bv) +{ + char *out; + char octet[4]; + ber_len_t i; + + log_debug("---BER value HEX dump (size %u bytes)", + (unsigned int) bv->bv_len); + + if (bv->bv_len > 0) { + out = (char *) calloc((3 * (bv->bv_len)) + 1, sizeof(char)); + if (!out) return; + + for (i = 1; i <= bv->bv_len; i++) { + snprintf(octet, 4, "%02x ", + (unsigned char) bv->bv_val[i - 1]); + strcat(out, octet); + } + log_debug(out); + free(out); + } +} + + diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.h b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.h new file mode 100644 index 00000000..c5722cbe --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-log.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + ***************************************************************************/ + +#ifndef _ZOS_REMOTE_LOG_H +#define _ZOS_REMOTE_LOG_H + +#include "zos-remote-ldap.h" + +#include +#include +#include +#include + +extern pid_t mypid; + +void log_err(const char *, ...); +void log_warn(const char *, ...); +void log_info(const char *, ...); +void _log_debug(const char *, ...); +void _debug_bv(struct berval *); +void _debug_ber(BerElement *); + +#ifdef DEBUG + +#define log_debug(fmt, ...) _log_debug(fmt, ## __VA_ARGS__) +#define debug_bv(bv) _debug_bv(bv) +#define debug_ber(ber) _debug_ber(ber) + +#else + +#define log_debug(fmt, ...) +#define debug_bv(bv) +#define debug_ber(ber) + +#endif /* DEBUG */ + + +#endif /* _ZOS_REMOTE_LOG_H */ diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c new file mode 100644 index 00000000..8234a273 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-plugin.c @@ -0,0 +1,580 @@ +/*************************************************************************** +* Copyright (C) 2007 International Business Machines Corp. * +* All Rights Reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +* * +* Authors: * +* Klaus Heinrich Kiwi * +***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBCAP_NG +#include +#endif +#include "auparse.h" +#include "zos-remote-log.h" +#include "zos-remote-ldap.h" +#include "zos-remote-config.h" +#include "zos-remote-queue.h" + +#define UNUSED(x) (void)(x) + +/* + * Global vars + */ +volatile int stop = 0; +volatile int hup = 0; +volatile ZOS_REMOTE zos_remote_inst; +static plugin_conf_t conf; +static const char *def_config_file = "/etc/audisp/zos-remote.conf"; +static pthread_t submission_thread; +pid_t mypid = 0; + +/* + * SIGTERM handler + */ +static void term_handler(int sig) +{ + UNUSED(sig); + log_info("Got Termination signal - shutting down plugin"); + stop = 1; + nudge_queue(); +} + +/* + * SIGHUP handler - re-read config, reconnect to ITDS + */ +static void hup_handler(int sig) +{ + UNUSED(sig); + log_info("Got Hangup signal - flushing plugin configuration"); + hup = 1; + nudge_queue(); +} + +/* + * SIGALRM handler - help force exit when terminating daemon + */ +static void alarm_handler(int sig) +{ + UNUSED(sig); + log_err("Timeout waiting for submission thread - Aborting (some events may have been dropped)"); + pthread_cancel(submission_thread); +} + +/* + * The submission thread + * It's job is to dequeue the events from the queue + * and sync submit them to ITDS + */ +static void *submission_thread_main(void *arg) +{ + int rc; + + UNUSED(arg); + log_debug("Starting event submission thread"); + + rc = zos_remote_init(&zos_remote_inst, conf.server, + conf.port, conf.user, + conf.password, + conf.timeout); + + if (rc != ICTX_SUCCESS) { + log_err("Error - Failed to initialize session to z/OS ITDS Server"); + stop = 1; + return 0; + } + + while (stop == 0) { + /* block until we have an event */ + BerElement *ber = dequeue(); + + if (ber == NULL) { + if (hup) { + break; + } + continue; + } + debug_ber(ber); + rc = submit_request_s(&zos_remote_inst, ber); + if (rc == ICTX_E_FATAL) { + log_err("Error - Fatal error in event submission. Aborting"); + stop = 1; + } else if (rc != ICTX_SUCCESS) { + log_warn("Warning - Event submission failure - event dropped"); + } + else { + log_debug("Event submission success"); + } + ber_free(ber, 1); /* also free BER buffer */ + } + log_debug("Stopping event submission thread"); + zos_remote_destroy(&zos_remote_inst); + + return 0; +} + + +/* + * auparse library callback that's called when an event is ready + */ +void +push_event(auparse_state_t * au, auparse_cb_event_t cb_event_type, + void *user_data) +{ + int rc; + BerElement *ber; + int qualifier; + char timestamp[26]; + char linkValue[ZOS_REMOTE_LINK_VALUE_SIZE]; + char logString[ZOS_REMOTE_LOGSTRING_SIZE]; + unsigned long linkValue_tmp; + + UNUSED(user_data); + if (cb_event_type != AUPARSE_CB_EVENT_READY) + return; + + const au_event_t *e = auparse_get_timestamp(au); + if (e == NULL) + return; + /* + * we have an event. Each record will result in a different 'Item' + * (refer ASN.1 definition in zos-remote-ldap.h) + */ + + /* + * Create a new BER element to encode the request + */ + ber = ber_alloc_t(LBER_USE_DER); + if (ber == NULL) { + log_err("Error allocating memory for BER element"); + goto fatal; + } + + /* + * Collect some information to fill in every item + */ + const char *node = auparse_get_node(au); + const char *orig_type = auparse_find_field(au, "type"); + /* roll back event to get 'success' */ + auparse_first_record(au); + const char *success = auparse_find_field(au, "success"); + /* roll back event to get 'res' */ + auparse_first_record(au); + const char *res = auparse_find_field(au, "res"); + + /* check if this event is a success or failure one */ + if (success) { + if (strncmp(success, "0", 1) == 0 || + strncmp(success, "no", 2) == 0) + qualifier = ZOS_REMOTE_QUALIF_FAIL; + else + qualifier = ZOS_REMOTE_QUALIF_SUCCESS; + } else if (res) { + if (strncmp(res, "0", 1) == 0 + || strncmp(res, "failed", 6) == 0) + qualifier = ZOS_REMOTE_QUALIF_FAIL; + else + qualifier = ZOS_REMOTE_QUALIF_SUCCESS; + } else + qualifier = ZOS_REMOTE_QUALIF_INFO; + + /* get timestamp text */ + ctime_r(&e->sec, timestamp); + timestamp[24] = '\0'; /* strip \n' */ + + /* prepare linkValue which will be used for every item */ + linkValue_tmp = htonl(e->serial); /* padronize to use network + * byte order + */ + memset(&linkValue, 0, ZOS_REMOTE_LINK_VALUE_SIZE); + memcpy(&linkValue, &linkValue_tmp, sizeof(unsigned long)); + + /* + * Prepare the logString with some meaningful text + * We assume the first record type found is the + * 'originating' audit record + */ + sprintf(logString, "Linux (%s): type: %s", node, orig_type); + free((void *)node); + + /* + * Start writing to BER element. + * There's only one field (version) out of the item sequence. + * Also open item sequence + */ + rc = ber_printf(ber, "{i{", ICTX_REQUESTVER); + if (rc < 0) + goto skip_event; + + /* + * Roll back to first record and iterate through all records + */ + auparse_first_record(au); + do { + const char *type = auparse_find_field(au, "type"); + if (type == NULL) + goto skip_event; + + log_debug("got record: %s", auparse_get_record_text(au)); + + /* + * First field is item Version, same as global version + */ + rc = ber_printf(ber, "{i", ICTX_REQUESTVER); + + /* + * Second field is the itemTag + * use our internal event counter, increasing it + */ + rc |= ber_printf(ber, "i", conf.counter++); + + /* + * Third field is the linkValue + * using ber_put_ostring since it is not null-terminated + */ + rc |= ber_put_ostring(ber, linkValue, + ZOS_REMOTE_LINK_VALUE_SIZE, + LBER_OCTETSTRING); + /* + * Fourth field is the violation + * Don't have anything better yet to put here + */ + rc |= ber_printf(ber, "b", 0); + + /* + * Fifth field is the event. + * FIXME: this might be the place to switch on the + * audit record type and map to a more meaningful + * SMF type 83, subtype 4 event here + */ + rc |= ber_printf(ber, "i", ZOS_REMOTE_EVENT_AUTHORIZATION); + + /* + * Sixth field is the qualifier. We map 'success' or + * 'res' to this field + */ + rc |= ber_printf(ber, "i", qualifier); + + /* + * Seventh field is the Class + * always use '@LINUX' for this version + * max size ZOS_REMOTE_CLASS_SIZE + */ + rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); + rc |= ber_printf(ber, "s", "@LINUX"); + + /* + * Eighth field is the resource + * use the record type (name) as the resource + * max size ZOS_REMOTE_RESOURCE_SIZE + */ + rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); + rc |= ber_printf(ber, "s", type); + + /* + * Nineth field is the LogString + * we try to put something meaningful here + * we also start the relocations sequence + */ + rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); + rc |= ber_printf(ber, "s{", logString); + + /* + * Now we start adding the relocations. + * Let's add the timestamp as the first one + * so it's out of the field loop + */ + rc |= ber_printf(ber, "{i", ZOS_REMOTE_RELOC_TIMESTAMP); + rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); + rc |= ber_printf(ber, "s}", timestamp); + + /* + * Check that encoding is going OK until now + */ + if (rc < 0) + goto skip_event; + + /* + * Now go to first field, + * and iterate through all fields + */ + auparse_first_field(au); + do { + /* + * we set a maximum of 1024 chars for + * relocation data (field=value pairs) + * Hopefuly this wont overflow too often + */ + char data[1024]; + const char *name = auparse_get_field_name(au); + const char *value = auparse_interpret_field(au); + if (name == NULL || value == NULL) + goto skip_event; + + /* + * First reloc field is the Relocation type + * We use 'OTHER' here since we don't have + * anything better + */ + rc |= ber_printf(ber, "{i", ZOS_REMOTE_RELOC_OTHER); + + /* + * Second field is the relocation data + * We use a 'name=value' pair here + * Use up to 1023 chars (one char left for '\0') + */ + snprintf(data, 1023, "%s=%s", name, value); + rc |= ber_printf(ber, "t", ASN1_IA5STRING_TAG); + rc |= ber_printf(ber, "s}", data); + + /* + * Check encoding status + */ + if (rc < 0) + goto skip_event; + } while (auparse_next_field(au) > 0); + + /* + * After adding all relocations we are done with + * this item - finalize relocs and item + */ + rc |= ber_printf(ber, "}}"); + + /* + * Check if we are doing well with encoding + */ + if (rc < 0) + goto skip_event; + + } while (auparse_next_record(au) > 0); + + /* + * We have all items in - finalize item sequence & request + */ + rc |= ber_printf(ber, "}}"); + + /* + * Check if everything went alright with encoding + */ + if (rc < 0) + goto skip_event; + + /* + * finally, enqueue request and let the other + * thread process it + */ + log_debug("Encoding done, enqueuing event"); + enqueue(ber); + + return; + +skip_event: + log_warn("Warning - error encoding request, skipping event"); + ber_free(ber, 1); /* free it since we're not enqueuing it */ + return; + +fatal: + log_err("Error - Fatal error while encoding request. Aborting"); + stop = 1; +} + +int main(int argc, char *argv[]) +{ + int rc; + const char *cpath; + char buf[1024]; + struct sigaction sa; + sigset_t ss; + auparse_state_t *au; + ssize_t len; + + mypid = getpid(); + + log_info("starting with pid=%d", mypid); + + /* + * install signal handlers + */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = term_handler; + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = hup_handler; + sigaction(SIGHUP, &sa, NULL); + sa.sa_handler = alarm_handler; + sigaction(SIGALRM, &sa, NULL); + + /* + * the main program accepts a single (optional) argument: + * it's configuration file (this is NOT the plugin configuration + * usually located at /etc/audisp/plugin.d) + * We use the default (def_config_file) if no arguments are given + */ + if (argc == 1) { + cpath = def_config_file; + log_warn("No configuration file specified - using default (%s)", cpath); + } else if (argc == 2) { + cpath = argv[1]; + log_info("Using configuration file: %s", cpath); + } else { + log_err("Error - invalid number of parameters passed. Aborting"); + return 1; + } + + /* initialize record counter */ + conf.counter = 1; + + /* initialize configuration with default values */ + plugin_clear_config(&conf); + + /* initialize the submission queue */ + if (init_queue(conf.q_depth) != 0) { + log_err("Error - Can't initialize event queue. Aborting"); + return -1; + } + +#ifdef HAVE_LIBCAP_NG + // Drop all capabilities + capng_clear(CAPNG_SELECT_BOTH); + capng_apply(CAPNG_SELECT_BOTH); +#endif + + /* set stdin to O_NONBLOCK */ + if (fcntl(0, F_SETFL, O_NONBLOCK) == -1) { + log_err("Error - Can't set input to Non-blocking mode: %s. Aborting", + strerror(errno)); + return -1; + } + + do { + + hup = 0; /* don't flush unless hup == 1 */ + + /* + * initialization is done in 4 steps: + */ + + /* + * load configuration and + * increase queue depth if needed + */ + rc = plugin_load_config(&conf, cpath); + if (rc != 0) { + log_err("Error - Can't load configuration. Aborting"); + return -1; + } + increase_queue_depth(conf.q_depth); /* 1 */ + + /* initialize auparse */ + au = auparse_init(AUSOURCE_FEED, 0); /* 2 */ + if (au == NULL) { + log_err("Error - exiting due to auparse init errors"); + return -1; + } + + /* + * Block signals for everyone, + * Initialize submission thread, and + * Unblock signals for this thread + */ + sigfillset(&ss); + pthread_sigmask(SIG_BLOCK, &ss, NULL); + pthread_create(&submission_thread, NULL, + submission_thread_main, NULL); + pthread_sigmask(SIG_UNBLOCK, &ss, NULL); /* 3 */ + + /* add our event consumer callback */ + auparse_add_callback(au, push_event, NULL, NULL); /* 4 */ + + /* main loop */ + while (hup == 0 && stop == 0) { + fd_set rfds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_SET(0, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + rc = select(1, &rfds, NULL, NULL, &tv); + if (rc == -1) { + if (errno == EINTR) { + log_debug("Select call interrupted"); + continue; + } + else { + log_err("Error - Fatal error while monitoring input: %s. Aborting", + strerror(errno)); + stop = 1; + } + } + else if (rc) { + len = read(0, buf, 1024); + if (len > 0) + /* let our callback know of the new data */ + auparse_feed(au, buf, len); + else if (len == 0) { + log_debug("End of input - Exiting"); + stop = 1; + } + else { + /* ignore interrupted call or empty pipe */ + if (errno != EINTR && errno != EAGAIN) { + log_err("Error - Fatal error while reading input: %s. Aborting", + strerror(errno)); + stop = 1; + } + else { + log_debug("Ignoring read interruption: %s", + strerror(errno)); + } + } + } + } + /* flush everything, in order */ + auparse_flush_feed(au); /* 4 */ + alarm(10); /* 10 seconds to clear the queue */ + pthread_join(submission_thread, NULL); /* 3 */ + alarm(0); /* cancel any pending alarm */ + auparse_destroy(au); /* 2 */ + plugin_free_config(&conf); /* 1 */ + } + while (hup && stop == 0); + + /* destroy queue before leaving */ + destroy_queue(); + + log_info("Exiting"); + + return 0; +} diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote-queue.c b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-queue.c new file mode 100644 index 00000000..8071dca4 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote-queue.c @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2007 International Business Machines Corp. * + * All Rights Reserved. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * * + * Authors: * + * Klaus Heinrich Kiwi * + * based on code by Steve Grubb * + ***************************************************************************/ + +#include "zos-remote-queue.h" + +#include +#include +#include +#include "zos-remote-log.h" + +static volatile BerElement **q; +static pthread_mutex_t queue_lock; +static pthread_cond_t queue_nonempty; +static unsigned int q_next, q_last, q_depth; + + +int init_queue(unsigned int size) +{ + unsigned int i; + + q_next = 0; + q_last = 0; + q_depth = size; + q = malloc(q_depth * sizeof(BerElement *)); + if (q == NULL) + return -1; + + for (i=0; i 3) { + log_err("queue is full - dropping event"); + return; + } + pthread_mutex_lock(&queue_lock); + + /* OK, have lock add event */ + n = q_next%q_depth; + if (q[n] == NULL) { + q[n] = ber; + q_next = (n+1) % q_depth; + pthread_cond_signal(&queue_nonempty); + pthread_mutex_unlock(&queue_lock); + } else { + pthread_mutex_unlock(&queue_lock); + pthread_yield(); /* Let dequeue thread run to clear queue */ + retry_cnt++; + goto retry; + } +} + +BerElement *dequeue(void) +{ + BerElement *ber; + unsigned int n; + + /* Wait until its got something in it */ + pthread_mutex_lock(&queue_lock); + n = q_last%q_depth; + if (q[n] == NULL) { + pthread_cond_wait(&queue_nonempty, &queue_lock); + n = q_last%q_depth; + } + + /* OK, grab the next event */ + if (q[n] != NULL) { + ber = (BerElement *) q[n]; + q[n] = NULL; + q_last = (n+1) % q_depth; + } else + ber = NULL; + + pthread_mutex_unlock(&queue_lock); + + /* Process the event */ + return ber; +} + +void nudge_queue(void) +{ + pthread_cond_signal(&queue_nonempty); +} + +void increase_queue_depth(unsigned int size) +{ + pthread_mutex_lock(&queue_lock); + if (size > q_depth) { + unsigned int i; + void *tmp_q; + + tmp_q = realloc(q, size * sizeof(BerElement *)); + q = tmp_q; + for (i=q_depth; i * + * based on code by Steve Grubb * + ***************************************************************************/ + +#ifndef _ZOS_REMOTE_QUEUE_H +#define _ZOS_REMOTE_QUEUE_H + +#include + +int init_queue(unsigned int size); +void enqueue(BerElement *); +BerElement *dequeue(void); +void nudge_queue(void); +void increase_queue_depth(unsigned int size); +void destroy_queue(void); + +#endif /* _ZOS_REMOTE_QUEUE_H */ + diff --git a/framework/src/audit/audisp/plugins/zos-remote/zos-remote.conf b/framework/src/audit/audisp/plugins/zos-remote/zos-remote.conf new file mode 100644 index 00000000..8cf85f71 --- /dev/null +++ b/framework/src/audit/audisp/plugins/zos-remote/zos-remote.conf @@ -0,0 +1,10 @@ +## This is the configuration file for the audispd-zos-remote +## Audit dispatcher plugin. +## See zos-remote.conf(5) for more information + +server = zos_server.localdomain +port = 389 +user = RACF_ID +password = racf_password +timeout = 15 +q_depth = 64 diff --git a/framework/src/audit/audisp/queue.c b/framework/src/audit/audisp/queue.c new file mode 100644 index 00000000..477b9d1b --- /dev/null +++ b/framework/src/audit/audisp/queue.c @@ -0,0 +1,226 @@ +/* queue.c -- + * Copyright 2007,2013,2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include "queue.h" + +static volatile event_t **q; +static pthread_mutex_t queue_lock; +static pthread_cond_t queue_nonempty; +static unsigned int q_next, q_last, q_depth, processing_suspended; +static const char *SINGLE = "1"; +static const char *HALT = "0"; +static int queue_full_warning = 0; +extern volatile int hup; +#define QUEUE_FULL_LIMIT 5 + +void reset_suspended(void) +{ + processing_suspended = 0; + queue_full_warning = 0; +} + +int init_queue(unsigned int size) +{ + unsigned int i; + + processing_suspended = 0; + q_next = 0; + q_last = 0; + q_depth = size; + q = malloc(q_depth * sizeof(event_t *)); + if (q == NULL) + return -1; + + for (i=0; ioverflow_action) + { + case O_IGNORE: + break; + case O_SYSLOG: + if (queue_full_warning < QUEUE_FULL_LIMIT) { + syslog(LOG_ERR, + "queue is full - dropping event"); + queue_full_warning++; + if (queue_full_warning == QUEUE_FULL_LIMIT) + syslog(LOG_ERR, + "audispd queue full reporting " + "limit reached - ending " + "dropped event notifications"); + } + break; + case O_SUSPEND: + syslog(LOG_ALERT, + "Audispd is suspending event processing due to overflowing its queue."); + processing_suspended = 1; + break; + case O_SINGLE: + syslog(LOG_ALERT, + "Audisp is now changing the system to single user mode due to overflowing its queue"); + change_runlevel(SINGLE); + break; + case O_HALT: + syslog(LOG_ALERT, + "Audispd is now halting the system due to overflowing its queue"); + change_runlevel(HALT); + break; + default: + syslog(LOG_ALERT, "Unknown overflow action requested"); + break; + } +} + +void enqueue(event_t *e, struct daemon_conf *config) +{ + unsigned int n, retry_cnt = 0; + + if (processing_suspended) { + free(e); + return; + } + +retry: + // We allow 3 retries and then its over + if (retry_cnt > 3) { + do_overflow_action(config); + free(e); + return; + } + pthread_mutex_lock(&queue_lock); + + // OK, have lock add event + n = q_next%q_depth; + if (q[n] == NULL) { + q[n] = e; + q_next = (n+1) % q_depth; + pthread_cond_signal(&queue_nonempty); + pthread_mutex_unlock(&queue_lock); + } else { + pthread_mutex_unlock(&queue_lock); + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 2 * 1000 * 1000; // 2 milliseconds + nanosleep(&ts, NULL); /* Let other thread try to log it. */ + retry_cnt++; + goto retry; + } +} + +event_t *dequeue(void) +{ + event_t *e; + unsigned int n; + + // Wait until its got something in it + pthread_mutex_lock(&queue_lock); + if (hup) { + pthread_mutex_unlock(&queue_lock); + return NULL; + } + n = q_last%q_depth; + if (q[n] == NULL) { + pthread_cond_wait(&queue_nonempty, &queue_lock); + n = q_last%q_depth; + } + + // OK, grab the next event + if (q[n] != NULL) { + e = (event_t *)q[n]; + q[n] = NULL; + q_last = (n+1) % q_depth; + } else + e = NULL; + + pthread_mutex_unlock(&queue_lock); + + // Process the event + return e; +} + +void nudge_queue(void) +{ + pthread_cond_signal(&queue_nonempty); +} + +void increase_queue_depth(unsigned int size) +{ + pthread_mutex_lock(&queue_lock); + if (size > q_depth) { + int i; + void *tmp_q; + + tmp_q = realloc(q, size * sizeof(event_t *)); + q = tmp_q; + for (i=q_depth; i + */ + +#ifndef QUEUE_HEADER +#define QUEUE_HEADER + +#include "libaudit.h" +#include "audispd-config.h" + +typedef struct event +{ + struct audit_dispatcher_header hdr; + char data[MAX_AUDIT_MESSAGE_LENGTH]; +} event_t; + + +void reset_suspended(void); +int init_queue(unsigned int size); +void enqueue(event_t *e, struct daemon_conf *config); +event_t *dequeue(void); +void nudge_queue(void); +void increase_queue_depth(unsigned int size); +void destroy_queue(void); + +#endif + diff --git a/framework/src/audit/audit.spec b/framework/src/audit/audit.spec new file mode 100644 index 00000000..ab07f326 --- /dev/null +++ b/framework/src/audit/audit.spec @@ -0,0 +1,301 @@ +%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} + +# Do we want systemd? +%if 0%{?!nosystemd:1} +%define WITH_SYSTEMD 1 +%else +%define WITH_SYSTEMD 0 +%endif + +Summary: User space tools for 2.6 kernel auditing +Name: audit +Version: 2.4.4 +Release: 1 +License: GPLv2+ +Group: System Environment/Daemons +URL: http://people.redhat.com/sgrubb/audit/ +Source0: http://people.redhat.com/sgrubb/audit/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root +BuildRequires: swig python-devel golang +BuildRequires: tcp_wrappers-devel krb5-devel libcap-ng-devel +BuildRequires: kernel-headers >= 2.6.29 +Requires: %{name}-libs = %{version}-%{release} +%if %{WITH_SYSTEMD} +BuildRequires: systemd-units +Requires(post): systemd-units systemd-sysv chkconfig coreutils +Requires(preun): systemd-units +Requires(postun): systemd-units coreutils +%else +Requires: chkconfig +%endif + +%description +The audit package contains the user space utilities for +storing and searching the audit records generate by +the audit subsystem in the Linux 2.6 kernel. + +%package libs +Summary: Dynamic library for libaudit +License: LGPLv2+ +Group: Development/Libraries + +%description libs +The audit-libs package contains the dynamic libraries needed for +applications to use the audit framework. + +%package libs-devel +Summary: Header files for libaudit +License: LGPLv2+ +Group: Development/Libraries +Requires: %{name}-libs = %{version}-%{release} +Requires: kernel-headers >= 2.6.29 + +%description libs-devel +The audit-libs-devel package contains the header files needed for +developing applications that need to use the audit framework libraries. + +%package libs-static +Summary: Static version of libaudit library +License: LGPLv2+ +Group: Development/Libraries +Requires: kernel-headers >= 2.6.29 + +%description libs-static +The audit-libs-static package contains the static libraries +needed for developing applications that need to use static audit +framework libraries + +%package libs-python +Summary: Python bindings for libaudit +License: LGPLv2+ +Group: Development/Libraries +Requires: %{name}-libs = %{version}-%{release} + +%description libs-python +The audit-libs-python package contains the bindings so that libaudit +and libauparse can be used by python. + +%package libs-python3 +Summary: Python3 bindings for libaudit +License: LGPLv2+ +Group: Development/Libraries +BuildRequires: python3-devel swig +Requires: %{name} = %{version}-%{release} + +%description libs-python3 +The audit-libs-python3 package contains the bindings so that libaudit +and libauparse can be used by python3. + +%package -n audispd-plugins +Summary: Plugins for the audit event dispatcher +License: GPLv2+ +Group: System Environment/Daemons +BuildRequires: openldap-devel +Requires: %{name} = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} +Requires: openldap + +%description -n audispd-plugins +The audispd-plugins package provides plugins for the real-time +interface to the audit system, audispd. These plugins can do things +like relay events to remote machines or analyze events for suspicious +behavior. + +%prep +%setup -q + +%build +%configure --sbindir=/sbin --libdir=/%{_lib} --with-python=yes --with-python3=yes --with-golang --with-libwrap --enable-gssapi-krb5=yes --enable-zos-remote --with-libcap-ng=yes \ +%if %{WITH_SYSTEMD} + --enable-systemd +%endif + +make %{?_smp_mflags} + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p $RPM_BUILD_ROOT/{sbin,etc/audispd/plugins.d} +%if !%{WITH_SYSTEMD} +mkdir -p $RPM_BUILD_ROOT/{etc/{sysconfig,rc.d/init.d}} +%endif +mkdir -p $RPM_BUILD_ROOT/%{_mandir}/{man5,man8} +mkdir -p $RPM_BUILD_ROOT/%{_lib} +mkdir -p $RPM_BUILD_ROOT/%{_libdir}/audit +mkdir -p $RPM_BUILD_ROOT/%{_var}/log/audit +mkdir -p $RPM_BUILD_ROOT/%{_var}/spool/audit +make DESTDIR=$RPM_BUILD_ROOT install + +mkdir -p $RPM_BUILD_ROOT/%{_libdir} +# This winds up in the wrong place when libtool is involved +mv $RPM_BUILD_ROOT/%{_lib}/libaudit.a $RPM_BUILD_ROOT%{_libdir} +mv $RPM_BUILD_ROOT/%{_lib}/libauparse.a $RPM_BUILD_ROOT%{_libdir} +curdir=`pwd` +cd $RPM_BUILD_ROOT/%{_libdir} +LIBNAME=`basename \`ls $RPM_BUILD_ROOT/%{_lib}/libaudit.so.1.*.*\`` +ln -s ../../%{_lib}/$LIBNAME libaudit.so +LIBNAME=`basename \`ls $RPM_BUILD_ROOT/%{_lib}/libauparse.so.0.*.*\`` +ln -s ../../%{_lib}/$LIBNAME libauparse.so +cd $curdir +# Remove these items so they don't get picked up. +rm -f $RPM_BUILD_ROOT/%{_lib}/libaudit.so +rm -f $RPM_BUILD_ROOT/%{_lib}/libauparse.so +rm -f $RPM_BUILD_ROOT/%{_lib}/libaudit.la +rm -f $RPM_BUILD_ROOT/%{_lib}/libauparse.la +rm -f $RPM_BUILD_ROOT/%{_libdir}/python?.?/site-packages/_audit.a +rm -f $RPM_BUILD_ROOT/%{_libdir}/python?.?/site-packages/_audit.la +rm -f $RPM_BUILD_ROOT/%{_libdir}/python?.?/site-packages/_auparse.a +rm -f $RPM_BUILD_ROOT/%{_libdir}/python?.?/site-packages/_auparse.la +rm -f $RPM_BUILD_ROOT/%{_libdir}/python?.?/site-packages/auparse.a +rm -f $RPM_BUILD_ROOT/%{_libdir}/python?.?/site-packages/auparse.la + +# Move the pkgconfig file +mv $RPM_BUILD_ROOT/%{_lib}/pkgconfig $RPM_BUILD_ROOT%{_libdir} + +# On platforms with 32 & 64 bit libs, we need to coordinate the timestamp +touch -r ./audit.spec $RPM_BUILD_ROOT/etc/libaudit.conf +touch -r ./audit.spec $RPM_BUILD_ROOT/usr/share/man/man5/libaudit.conf.5.gz + +%check +make check + +%clean +rm -rf $RPM_BUILD_ROOT + +%post libs -p /sbin/ldconfig + +%post +# Copy default rules into place on new installation +if [ ! -e /etc/audit/audit.rules ] ; then + cp /etc/audit/rules.d/audit.rules /etc/audit/audit.rules +fi +%if %{WITH_SYSTEMD} +%systemd_post auditd.service +%else +/sbin/chkconfig --add auditd +%endif + +%preun +%if %{WITH_SYSTEMD} +%systemd_preun auditd.service +%else +if [ $1 -eq 0 ]; then + /sbin/service auditd stop > /dev/null 2>&1 + /sbin/chkconfig --del auditd +fi +%endif + +%postun libs -p /sbin/ldconfig + +%postun +if [ $1 -ge 1 ]; then + /sbin/service auditd condrestart > /dev/null 2>&1 || : +fi + +%files libs +%defattr(-,root,root,-) +/%{_lib}/libaudit.so.1* +/%{_lib}/libauparse.* +%config(noreplace) %attr(640,root,root) /etc/libaudit.conf +%{_mandir}/man5/libaudit.conf.5.gz + +%files libs-devel +%defattr(-,root,root,-) +%doc contrib/skeleton.c contrib/plugin +%{_libdir}/libaudit.so +%{_libdir}/libauparse.so +%dir %{_prefix}/lib/golang/src/pkg/redhat.com/audit +%{_prefix}/lib/golang/src/pkg/redhat.com/audit/audit.go +%{_includedir}/libaudit.h +%{_includedir}/auparse.h +%{_includedir}/auparse-defs.h +%{_libdir}/pkgconfig/audit.pc +%{_libdir}/pkgconfig/auparse.pc +%{_mandir}/man3/* + +%files libs-static +%defattr(-,root,root,-) +%{_libdir}/libaudit.a +%{_libdir}/libauparse.a + +%files libs-python +%defattr(-,root,root,-) +%attr(755,root,root) %{python_sitearch}/_audit.so +%attr(755,root,root) %{python_sitearch}/auparse.so +%{python_sitearch}/audit.py* + +%files libs-python3 +%defattr(-,root,root,-) +%attr(755,root,root) %{python3_sitearch}/* + +%files +%defattr(-,root,root,-) +%doc README COPYING ChangeLog contrib/capp.rules contrib/nispom.rules contrib/lspp.rules contrib/stig.rules init.d/auditd.cron +%attr(644,root,root) %{_mandir}/man8/audispd.8.gz +%attr(644,root,root) %{_mandir}/man8/auditctl.8.gz +%attr(644,root,root) %{_mandir}/man8/auditd.8.gz +%attr(644,root,root) %{_mandir}/man8/aureport.8.gz +%attr(644,root,root) %{_mandir}/man8/ausearch.8.gz +%attr(644,root,root) %{_mandir}/man8/autrace.8.gz +%attr(644,root,root) %{_mandir}/man8/aulast.8.gz +%attr(644,root,root) %{_mandir}/man8/aulastlog.8.gz +%attr(644,root,root) %{_mandir}/man8/auvirt.8.gz +%attr(644,root,root) %{_mandir}/man8/augenrules.8.gz +%attr(644,root,root) %{_mandir}/man8/ausyscall.8.gz +%attr(644,root,root) %{_mandir}/man7/audit.rules.7.gz +%attr(644,root,root) %{_mandir}/man5/auditd.conf.5.gz +%attr(644,root,root) %{_mandir}/man5/audispd.conf.5.gz +%attr(644,root,root) %{_mandir}/man5/ausearch-expression.5.gz +%attr(750,root,root) /sbin/auditctl +%attr(750,root,root) /sbin/auditd +%attr(755,root,root) /sbin/ausearch +%attr(755,root,root) /sbin/aureport +%attr(750,root,root) /sbin/autrace +%attr(750,root,root) /sbin/audispd +%attr(750,root,root) /sbin/augenrules +%attr(755,root,root) %{_bindir}/aulast +%attr(755,root,root) %{_bindir}/aulastlog +%attr(755,root,root) %{_bindir}/ausyscall +%attr(755,root,root) %{_bindir}/auvirt +%if %{WITH_SYSTEMD} +%attr(640,root,root) %{_unitdir}/auditd.service +%attr(750,root,root) %dir %{_libexecdir}/initscripts/legacy-actions/auditd +%attr(750,root,root) %{_libexecdir}/initscripts/legacy-actions/auditd/resume +%attr(750,root,root) %{_libexecdir}/initscripts/legacy-actions/auditd/rotate +%attr(750,root,root) %{_libexecdir}/initscripts/legacy-actions/auditd/stop +%attr(750,root,root) %{_libexecdir}/initscripts/legacy-actions/auditd/restart +%attr(750,root,root) %{_libexecdir}/initscripts/legacy-actions/auditd/condrestart +%else +%attr(755,root,root) /etc/rc.d/init.d/auditd +%config(noreplace) %attr(640,root,root) /etc/sysconfig/auditd +%endif +%attr(750,root,root) %dir %{_var}/log/audit +%attr(750,root,root) %dir /etc/audit +%attr(750,root,root) %dir /etc/audit/rules.d +%attr(750,root,root) %dir /etc/audisp +%attr(750,root,root) %dir /etc/audisp/plugins.d +%config(noreplace) %attr(640,root,root) /etc/audit/auditd.conf +%config(noreplace) %attr(640,root,root) /etc/audit/rules.d/audit.rules +%ghost %config(noreplace) %attr(640,root,root) /etc/audit/audit.rules +%config(noreplace) %attr(640,root,root) /etc/audisp/audispd.conf +%config(noreplace) %attr(640,root,root) /etc/audisp/plugins.d/af_unix.conf +%config(noreplace) %attr(640,root,root) /etc/audisp/plugins.d/syslog.conf + +%files -n audispd-plugins +%defattr(-,root,root,-) +%attr(644,root,root) %{_mandir}/man8/audispd-zos-remote.8.gz +%attr(644,root,root) %{_mandir}/man5/zos-remote.conf.5.gz +%config(noreplace) %attr(640,root,root) /etc/audisp/plugins.d/audispd-zos-remote.conf +%config(noreplace) %attr(640,root,root) /etc/audisp/zos-remote.conf +%attr(750,root,root) /sbin/audispd-zos-remote +%config(noreplace) %attr(640,root,root) /etc/audisp/audisp-remote.conf +%config(noreplace) %attr(640,root,root) /etc/audisp/plugins.d/au-remote.conf +%attr(750,root,root) /sbin/audisp-remote +%attr(700,root,root) %dir %{_var}/spool/audit +%attr(644,root,root) %{_mandir}/man5/audisp-remote.conf.5.gz +%attr(644,root,root) %{_mandir}/man8/audisp-remote.8.gz + + +%changelog +* Thu Aug 13 2015 Steve Grubb 2.4.4-1 +- New upstream release + diff --git a/framework/src/audit/auparse/Makefile.am b/framework/src/audit/auparse/Makefile.am new file mode 100644 index 00000000..4b864d7c --- /dev/null +++ b/framework/src/audit/auparse/Makefile.am @@ -0,0 +1,491 @@ +# Makefile.am -- +# Copyright 2006-08,2011-15 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +SUBDIRS = test +CLEANFILES = $(BUILT_SOURCES) +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AM_CFLAGS = -fPIC -DPIC -D_GNU_SOURCE -g ${DEBUG} +AM_CPPFLAGS = -I. -I${top_srcdir} -I${top_srcdir}/src -I${top_srcdir}/lib +LIBS = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = auparse.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libauparse.la +include_HEADERS = auparse.h auparse-defs.h +libauparse_la_SOURCES = nvpair.c interpret.c nvlist.c ellist.c \ + auparse.c auditd-config.c message.c data_buf.c strsplit.c \ + auparse-defs.h auparse-idata.h data_buf.h \ + nvlist.h auparse.h ellist.h \ + internal.h nvpair.h rnode.h interpret.h \ + private.h expression.c expression.h tty_named_keys.h +nodist_libauparse_la_SOURCES = $(BUILT_SOURCES) + +libauparse_la_LIBADD = ${top_builddir}/lib/libaudit.la +libauparse_la_DEPENDENCIES = $(libauparse_la_SOURCES) ${top_builddir}/config.h +libauparse_la_LDFLAGS = -Wl,-z,relro + +message.c: + cp ${top_srcdir}/lib/message.c . + +strsplit.c: + cp ${top_srcdir}/lib/strsplit.c . + +BUILT_SOURCES = accesstabs.h captabs.h clocktabs.h clone-flagtabs.h \ + epoll_ctls.h famtabs.h fcntl-cmdtabs.h \ + flagtabs.h icmptypetabs.h ipctabs.h ipccmdtabs.h\ + ioctlreqtabs.h ipoptnametabs.h ip6optnametabs.h \ + mmaptabs.h mounttabs.h nfprototabs.h open-flagtabs.h \ + persontabs.h prctl_opttabs.h pktoptnametabs.h \ + prottabs.h ptracetabs.h \ + rlimittabs.h recvtabs.h schedtabs.h seccomptabs.h \ + seektabs.h shm_modetabs.h signaltabs.h sockoptnametabs.h \ + socktabs.h sockleveltabs.h socktypetabs.h \ + tcpoptnametabs.h typetabs.h umounttabs.h +noinst_PROGRAMS = gen_accesstabs_h gen_captabs_h gen_clock_h \ + gen_clone-flagtabs_h \ + gen_epoll_ctls_h gen_famtabs_h \ + gen_fcntl-cmdtabs_h gen_flagtabs_h gen_ioctlreqtabs_h \ + gen_icmptypetabs_h gen_ipctabs_h gen_ipccmdtabs_h\ + gen_ipoptnametabs_h gen_ip6optnametabs_h gen_nfprototabs_h \ + gen_mmaptabs_h gen_mounttabs_h \ + gen_open-flagtabs_h gen_persontabs_h \ + gen_prctl_opttabs_h gen_pktoptnametabs_h gen_prottabs_h \ + gen_recvtabs_h gen_rlimit_h gen_ptracetabs_h \ + gen_schedtabs_h gen_seccomptabs_h \ + gen_seektabs_h gen_shm_modetabs_h gen_signals_h \ + gen_sockoptnametabs_h gen_socktabs_h gen_sockleveltabs_h \ + gen_socktypetabs_h gen_tcpoptnametabs_h gen_typetabs_h \ + gen_umounttabs_h + +gen_accesstabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h accesstab.h +gen_accesstabs_h_CFLAGS = '-DTABLE_H="accesstab.h"' +$(gen_accesstabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_accesstabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_accesstabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_accesstabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_accesstabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_accesstabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +accesstabs.h: gen_accesstabs_h Makefile + ./gen_accesstabs_h --i2s-transtab access > $@ + +gen_captabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h captab.h +gen_captabs_h_CFLAGS = '-DTABLE_H="captab.h"' +$(gen_captabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_captabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_captabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_captabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_captabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_captabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +captabs.h: gen_captabs_h Makefile + ./gen_captabs_h --i2s cap > $@ + +gen_clock_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h clocktab.h +gen_clock_h_CFLAGS = '-DTABLE_H="clocktab.h"' +$(gen_clock_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_clock_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_clock_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_clock_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_clock_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_clock_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +clocktabs.h: gen_clock_h Makefile + ./gen_clock_h --i2s clock > $@ + +gen_clone_flagtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h \ + clone-flagtab.h +gen_clone_flagtabs_h_CFLAGS = '-DTABLE_H="clone-flagtab.h"' +$(gen_clone_flagtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_clone_flagtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_clone_flagtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_clone-flagtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_clone-flagtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_clone-flagtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +clone-flagtabs.h: gen_clone-flagtabs_h Makefile + ./gen_clone-flagtabs_h --i2s-transtab clone_flag > $@ + +gen_epoll_ctls_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h epoll_ctl.h +gen_epoll_ctls_h_CFLAGS = '-DTABLE_H="epoll_ctl.h"' +$(gen_epoll_ctls_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_epoll_ctls_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_epoll_ctls_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_epoll_ctls_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_epoll_ctls_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_epoll_ctls_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +epoll_ctls.h: gen_epoll_ctls_h Makefile + ./gen_epoll_ctls_h --i2s epoll_ctl > $@ + +gen_famtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h famtab.h +gen_famtabs_h_CFLAGS = '-DTABLE_H="famtab.h"' +$(gen_famtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_famtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_famtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_famtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_famtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_famtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +famtabs.h: gen_famtabs_h Makefile + ./gen_famtabs_h --i2s fam > $@ + +gen_flagtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h flagtab.h +# ../auparse/ is used to avoid using ../lib/flagtab.h +gen_flagtabs_h_CFLAGS = '-DTABLE_H="../auparse/flagtab.h"' +$(gen_flagtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_flagtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_flagtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_flagtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_flagtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_flagtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +flagtabs.h: gen_flagtabs_h Makefile + ./gen_flagtabs_h --i2s-transtab flag > $@ + +gen_fcntl_cmdtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h \ + fcntl-cmdtab.h +gen_fcntl_cmdtabs_h_CFLAGS = '-DTABLE_H="fcntl-cmdtab.h"' +$(gen_fcntl_cmdtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_fcntl_cmdtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_fcntl_cmdtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_fcntl-cmdtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_fcntl-cmdtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_fcntl-cmdtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +fcntl-cmdtabs.h: gen_fcntl-cmdtabs_h Makefile + ./gen_fcntl-cmdtabs_h --i2s fcntl > $@ + +gen_icmptypetabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h icmptypetab.h +gen_icmptypetabs_h_CFLAGS = '-DTABLE_H="icmptypetab.h"' +$(gen_icmptypetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_icmptypetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_icmptypetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_icmptypetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_icmptypetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_icmptypetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +icmptypetabs.h: gen_icmptypetabs_h Makefile + ./gen_icmptypetabs_h --i2s icmptype > $@ + +gen_ioctlreqtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h ioctlreqtab.h +gen_ioctlreqtabs_h_CFLAGS = '-DTABLE_H="ioctlreqtab.h"' +$(gen_ioctlreqtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ioctlreqtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ioctlreqtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ioctlreqtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ioctlreqtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ioctlreqtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ioctlreqtabs.h: gen_ioctlreqtabs_h Makefile + ./gen_ioctlreqtabs_h --i2s ioctlreq > $@ + +gen_ipctabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h ipctab.h +gen_ipctabs_h_CFLAGS = '-DTABLE_H="ipctab.h"' +$(gen_ipctabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ipctabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ipctabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ipctabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ipctabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ipctabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ipctabs.h: gen_ipctabs_h Makefile + ./gen_ipctabs_h --i2s ipc > $@ + +gen_ipccmdtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h ipccmdtab.h +gen_ipccmdtabs_h_CFLAGS = '-DTABLE_H="ipccmdtab.h"' +$(gen_ipccmdtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ipccmdtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ipccmdtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ipccmdtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ipccmdtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ipccmdtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ipccmdtabs.h: gen_ipccmdtabs_h Makefile + ./gen_ipccmdtabs_h --i2s-transtab ipccmd > $@ + +gen_ipoptnametabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h ipoptnametab.h +gen_ipoptnametabs_h_CFLAGS = '-DTABLE_H="ipoptnametab.h"' +$(gen_ipoptnametabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ipoptnametabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ipoptnametabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ipoptnametabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ipoptnametabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ipoptnametabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ipoptnametabs.h: gen_ipoptnametabs_h Makefile + ./gen_ipoptnametabs_h --i2s ipoptname > $@ + +gen_ip6optnametabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h ip6optnametab.h +gen_ip6optnametabs_h_CFLAGS = '-DTABLE_H="ip6optnametab.h"' +$(gen_ip6optnametabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ip6optnametabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ip6optnametabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ip6optnametabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ip6optnametabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ip6optnametabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ip6optnametabs.h: gen_ip6optnametabs_h Makefile + ./gen_ip6optnametabs_h --i2s ip6optname > $@ + +gen_mmaptabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h mmaptab.h +gen_mmaptabs_h_CFLAGS = '-DTABLE_H="mmaptab.h"' +$(gen_mmaptabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_mmaptabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_mmaptabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_mmaptabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_mmaptabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_mmaptabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +mmaptabs.h: gen_mmaptabs_h Makefile + ./gen_mmaptabs_h --i2s-transtab mmap > $@ + +gen_mounttabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h mounttab.h +gen_mounttabs_h_CFLAGS = '-DTABLE_H="mounttab.h"' +$(gen_mounttabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_mounttabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_mounttabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_mounttabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_mounttabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_mounttabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +mounttabs.h: gen_mounttabs_h Makefile + ./gen_mounttabs_h --i2s-transtab mount > $@ + +gen_nfprototabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h nfprototab.h +gen_nfprototabs_h_CFLAGS = '-DTABLE_H="nfprototab.h"' +$(gen_nfprototabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_nfprototabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_nfprototabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_nfprototabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_nfprototabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_nfprototabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +nfprototabs.h: gen_nfprototabs_h Makefile + ./gen_nfprototabs_h --i2s nfproto > $@ + +gen_open_flagtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h \ + open-flagtab.h +gen_open_flagtabs_h_CFLAGS = '-DTABLE_H="open-flagtab.h"' +$(gen_open_flagtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_open_flagtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_open_flagtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_open-flagtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_open-flagtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_open-flagtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +open-flagtabs.h: gen_open-flagtabs_h Makefile + ./gen_open-flagtabs_h --i2s-transtab open_flag > $@ + +gen_persontabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h persontab.h +gen_persontabs_h_CFLAGS = '-DTABLE_H="persontab.h"' +$(gen_persontabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_persontabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_persontabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_persontabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_persontabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_persontabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +persontabs.h: gen_persontabs_h Makefile + ./gen_persontabs_h --i2s person > $@ + +gen_ptracetabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h ptracetab.h +gen_ptracetabs_h_CFLAGS = '-DTABLE_H="ptracetab.h"' +$(gen_ptracetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ptracetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ptracetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ptracetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ptracetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ptracetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ptracetabs.h: gen_ptracetabs_h Makefile + ./gen_ptracetabs_h --i2s ptrace > $@ + +gen_prctl_opttabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h prctl-opt-tab.h +gen_prctl_opttabs_h_CFLAGS = '-DTABLE_H="prctl-opt-tab.h"' +$(gen_prctl_opttabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_prctl_opttabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_prctl_opttabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_prctl_opttabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_prctl_opttabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_prctl_opttabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +prctl_opttabs.h: gen_prctl_opttabs_h Makefile + ./gen_prctl_opttabs_h --i2s prctl_opt > $@ + +gen_pktoptnametabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h pktoptnametab.h +gen_pktoptnametabs_h_CFLAGS = '-DTABLE_H="pktoptnametab.h"' +$(gen_pktoptnametabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_pktoptnametabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_pktoptnametabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_pktoptnametabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_pktoptnametabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_pktoptnametabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +pktoptnametabs.h: gen_pktoptnametabs_h Makefile + ./gen_pktoptnametabs_h --i2s pktoptname > $@ + +gen_prottabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h prottab.h +gen_prottabs_h_CFLAGS = '-DTABLE_H="prottab.h"' +$(gen_prottabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_prottabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_prottabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_prottabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_prottabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_prottabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +prottabs.h: gen_prottabs_h Makefile + ./gen_prottabs_h --i2s-transtab prot > $@ + +gen_recvtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h recvtab.h +gen_recvtabs_h_CFLAGS = '-DTABLE_H="recvtab.h"' +$(gen_recvtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_recvtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_recvtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_recvtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_recvtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_recvtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +recvtabs.h: gen_recvtabs_h Makefile + ./gen_recvtabs_h --i2s-transtab recv > $@ + +gen_rlimit_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h rlimittab.h +gen_rlimit_h_CFLAGS = '-DTABLE_H="rlimittab.h"' +$(gen_rlimit_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_rlimit_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_rlimit_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_rlimit_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_rlimit_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_rlimit_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +rlimittabs.h: gen_rlimit_h Makefile + ./gen_rlimit_h --i2s rlimit > $@ + +gen_schedtabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h schedtab.h +gen_schedtabs_h_CFLAGS = '-DTABLE_H="schedtab.h"' +$(gen_schedtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_schedtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_schedtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_schedtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_schedtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_schedtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +schedtabs.h: gen_schedtabs_h Makefile + ./gen_schedtabs_h --i2s sched > $@ + +gen_seccomptabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h seccomptab.h +gen_seccomptabs_h_CFLAGS = '-DTABLE_H="seccomptab.h"' +$(gen_seccomptabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_seccomptabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_seccomptabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_seccomptabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_seccomptabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_seccomptabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +seccomptabs.h: gen_seccomptabs_h Makefile + ./gen_seccomptabs_h --i2s seccomp > $@ + +gen_seektabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h seektab.h +gen_seektabs_h_CFLAGS = '-DTABLE_H="seektab.h"' +$(gen_seektabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_seektabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_seektabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_seektabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_seektabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_seektabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +seektabs.h: gen_seektabs_h Makefile + ./gen_seektabs_h --i2s seek > $@ + +gen_shm_modetabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h shm_modetab.h +gen_shm_modetabs_h_CFLAGS = '-DTABLE_H="shm_modetab.h"' +$(gen_shm_modetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_shm_modetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_shm_modetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_shm_modetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_shm_modetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_shm_modetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +shm_modetabs.h: gen_shm_modetabs_h Makefile + ./gen_shm_modetabs_h --i2s-transtab shm_mode > $@ + +gen_signals_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h signaltab.h +gen_signals_h_CFLAGS = '-DTABLE_H="signaltab.h"' +$(gen_signals_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_signals_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_signals_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_signals_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_signals_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_signals_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +signaltabs.h: gen_signals_h Makefile + ./gen_signals_h --i2s signal > $@ + +gen_sockleveltabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h sockleveltab.h +gen_sockleveltabs_h_CFLAGS = '-DTABLE_H="sockleveltab.h"' +$(gen_sockleveltabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_sockleveltabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_sockleveltabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_sockleveltabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_sockleveltabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_sockleveltabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +sockleveltabs.h: gen_sockleveltabs_h Makefile + ./gen_sockleveltabs_h --i2s socklevel > $@ + +gen_sockoptnametabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h sockoptnametab.h +gen_sockoptnametabs_h_CFLAGS = '-DTABLE_H="sockoptnametab.h"' +$(gen_sockoptnametabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_sockoptnametabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_sockoptnametabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_sockoptnametabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_sockoptnametabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_sockoptnametabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +sockoptnametabs.h: gen_sockoptnametabs_h Makefile + ./gen_sockoptnametabs_h --i2s sockoptname > $@ + +gen_socktabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h socktab.h +gen_socktabs_h_CFLAGS = '-DTABLE_H="socktab.h"' +$(gen_socktabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_socktabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_socktabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_socktabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_socktabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_socktabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +socktabs.h: gen_socktabs_h Makefile + ./gen_socktabs_h --i2s sock > $@ + +gen_socktypetabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h socktypetab.h +gen_socktypetabs_h_CFLAGS = '-DTABLE_H="socktypetab.h"' +$(gen_socktypetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_socktypetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_socktypetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_socktypetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_socktypetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_socktypetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +socktypetabs.h: gen_socktypetabs_h Makefile + ./gen_socktypetabs_h --i2s sock_type > $@ + +gen_tcpoptnametabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h tcpoptnametab.h +gen_tcpoptnametabs_h_CFLAGS = '-DTABLE_H="tcpoptnametab.h"' +$(gen_tcpoptnametabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_tcpoptnametabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_tcpoptnametabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_tcpoptnametabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_tcpoptnametabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_tcpoptnametabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +tcpoptnametabs.h: gen_tcpoptnametabs_h Makefile + ./gen_tcpoptnametabs_h --i2s tcpoptname > $@ + +gen_typetabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h typetab.h +gen_typetabs_h_CFLAGS = '-DTABLE_H="typetab.h"' +$(gen_typetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_typetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_typetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_typetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_typetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_typetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +typetabs.h: gen_typetabs_h Makefile + ./gen_typetabs_h --s2i type > $@ + +gen_umounttabs_h_SOURCES = ../lib/gen_tables.c ../lib/gen_tables.h umounttab.h +gen_umounttabs_h_CFLAGS = '-DTABLE_H="umounttab.h"' +$(gen_umounttabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_umounttabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_umounttabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_umounttabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_umounttabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_umounttabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +umounttabs.h: gen_umounttabs_h Makefile + ./gen_umounttabs_h --i2s-transtab umount > $@ + diff --git a/framework/src/audit/auparse/accesstab.h b/framework/src/audit/auparse/accesstab.h new file mode 100644 index 00000000..439c26d0 --- /dev/null +++ b/framework/src/audit/auparse/accesstab.h @@ -0,0 +1,27 @@ +/* accesstab.h -- + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + + +_S(0x1U, "X_OK" ) +_S(0x2U, "W_OK" ) +_S(0x4U, "R_OK" ) + diff --git a/framework/src/audit/auparse/auditd-config.c b/framework/src/audit/auparse/auditd-config.c new file mode 100644 index 00000000..5964538f --- /dev/null +++ b/framework/src/audit/auparse/auditd-config.c @@ -0,0 +1,445 @@ +/* auditd-config.c -- + * Copyright 2007,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Local prototypes */ +struct _pair +{ + const char *name; + const char *value; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(const char *, int, struct daemon_conf *); +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file); +static int nv_split(char *buf, struct _pair *nv); +static const struct kw_pair *kw_lookup(const char *val); +static int log_file_parser(const char *val, int line, + struct daemon_conf *config); +static int num_logs_parser(const char *val, int line, + struct daemon_conf *config); +static int log_format_parser(const char *val, int line, + struct daemon_conf *config); + +static const struct kw_pair keywords[] = +{ + {"log_file", log_file_parser }, + {"log_format", log_format_parser }, + {"num_logs", num_logs_parser }, + { NULL, NULL } +}; + +static const struct nv_list log_formats[] = +{ + {"raw", LF_RAW }, + {"nolog", LF_NOLOG }, + { NULL, 0 } +}; + + +/* + * Set everything to its default value +*/ +void clear_config(struct daemon_conf *config) +{ + config->qos = QOS_NON_BLOCKING; + config->sender_uid = 0; + config->sender_pid = 0; + config->sender_ctx = NULL; + config->log_file = strdup("/var/log/audit/audit.log"); + config->log_format = LF_RAW; + config->log_group = 0; + config->priority_boost = 3; + config->flush = FT_NONE; + config->freq = 0; + config->num_logs = 0L; + config->dispatcher = NULL; + config->node_name_format = N_NONE; + config->node_name = NULL; + config->max_log_size = 0L; + config->max_log_size_action = SZ_IGNORE; + config->space_left = 0L; + config->space_left_action = FA_IGNORE; + config->space_left_exe = NULL; + config->action_mail_acct = strdup("root"); + config->admin_space_left= 0L; + config->admin_space_left_action = FA_IGNORE; + config->admin_space_left_exe = NULL; + config->disk_full_action = FA_IGNORE; + config->disk_full_exe = NULL; + config->disk_error_action = FA_SYSLOG; + config->disk_error_exe = NULL; +} + +int load_config(struct daemon_conf *config, log_test_t lt) +{ + int fd, rc, lineno = 1; + struct stat st; + FILE *f; + char buf[160]; + + clear_config(config); + lt = lt; + + /* open the file */ + rc = open(CONFIG_FILE, O_RDONLY|O_NOFOLLOW); + if (rc < 0) { + if (errno != ENOENT) { + audit_msg(LOG_ERR, "Error opening config file (%s)", + strerror(errno)); + return 1; + } + audit_msg(LOG_WARNING, + "Config file %s doesn't exist, skipping", CONFIG_FILE); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + if (fstat(fd, &st) < 0) { + audit_msg(LOG_ERR, "Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + audit_msg(LOG_ERR, "Error - %s isn't owned by root", + CONFIG_FILE); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + audit_msg(LOG_ERR, "Error - %s is not a regular file", + CONFIG_FILE); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + audit_msg(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf, sizeof(buf), &lineno, CONFIG_FILE)) { + // convert line into name-value pair + const struct kw_pair *kw; + struct _pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + audit_msg(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, CONFIG_FILE); + break; + case 2: // no '=' sign + audit_msg(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, CONFIG_FILE); + break; + default: // something else went wrong... + audit_msg(LOG_ERR, + "Unknown error for line %d in %s", + lineno, CONFIG_FILE); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + audit_msg(LOG_ERR, + "Not processing any more lines in %s", + CONFIG_FILE); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name) { + /* dispatch to keyword's local parser */ + rc = kw->parser(nv.value, lineno, config); + if (rc != 0) { + fclose(f); + return 1; // local parser puts message out + } + } + + lineno++; + } + + fclose(f); + return 0; +} + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file) +{ + int too_long = 0; + + while (fgets_unlocked(buf, size, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) { + if (!too_long) { + *ptr = 0; + return buf; + } + // Reset and start with the next line + too_long = 0; + *lineno = *lineno + 1; + } else { + // If a line is too long skip it. + // Only output 1 warning + if (!too_long) + audit_msg(LOG_ERR, + "Skipping line %d in %s: too long", + *lineno, file); + too_long = 1; + } + } + return NULL; +} + +static int nv_split(char *buf, struct _pair *nv) +{ + /* Get the name part */ + char *ptr; + + nv->name = NULL; + nv->value = NULL; + ptr = audit_strsplit(buf); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = audit_strsplit(NULL); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = audit_strsplit(NULL); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* Make sure there's nothing else */ + ptr = audit_strsplit(NULL); + if (ptr) { + /* Allow one option, but check that there's not 2 */ + ptr = audit_strsplit(NULL); + if (ptr) + return 1; + } + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int log_file_parser(const char *val, int line,struct daemon_conf *config) +{ + char *dir = NULL, *tdir, *base; + DIR *d; + int fd, mode; + struct stat buf; + + /* split name into dir and basename. */ + tdir = strdup(val); + if (tdir) + dir = dirname(tdir); + if (dir == NULL || strlen(dir) < 4) { // '/var' is shortest dirname + audit_msg(LOG_ERR, + "The directory name: %s is too short - line %d", + dir, line); + free((void *)tdir); + return 1; + } + + base = basename((char *)val); + if (base == 0 || strlen(base) == 0) { + audit_msg(LOG_ERR, "The file name: %s is too short - line %d", + base, line); + free((void *)tdir); + return 1; + } + + /* verify the directory path exists */ + d = opendir(dir); + if (d == NULL) { + audit_msg(LOG_ERR, "Could not open dir %s (%s)", dir, + strerror(errno)); + free((void *)tdir); + return 1; + } + free((void *)tdir); + closedir(d); + + /* if the file exists, see that its regular, owned by root, + * and not world anything */ + mode = O_RDONLY; + + fd = open(val, mode); + if (fd < 0) { + audit_msg(LOG_ERR, "Unable to open %s (%s)", val, + strerror(errno)); + return 1; + } + if (fstat(fd, &buf) < 0) { + audit_msg(LOG_ERR, "Unable to stat %s (%s)", + val, strerror(errno)); + close(fd); + return 1; + } + close(fd); + if (!S_ISREG(buf.st_mode)) { + audit_msg(LOG_ERR, "%s is not a regular file", val); + return 1; + } + if (buf.st_uid != 0) { + audit_msg(LOG_ERR, "%s is not owned by root", val); + return 1; + } + if ( (buf.st_mode & (S_IXUSR|S_IWGRP|S_IXGRP|S_IRWXO)) ) { + audit_msg(LOG_ERR, "%s permissions should be 0600 or 0640", + val); + return 1; + } + if ( !(buf.st_mode & S_IWUSR) ) { + audit_msg(LOG_ERR, "audit log is not writable by owner"); + return 1; + } + + free((void *)config->log_file); + config->log_file = strdup(val); + if (config->log_file == NULL) + return 1; + return 0; +} + +static int num_logs_parser(const char *val, int line, + struct daemon_conf *config) +{ + const char *ptr = val; + unsigned long i; + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + val, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(val, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + if (i > 99) { + audit_msg(LOG_ERR, "num_logs must be 99 or less"); + return 1; + } + config->num_logs = i; + return 0; +} + +static int log_format_parser(const char *val, int line, + struct daemon_conf *config) +{ + int i; + + for (i=0; log_formats[i].name != NULL; i++) { + if (strcasecmp(val, log_formats[i].name) == 0) { + config->log_format = log_formats[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", val, line); + return 1; +} + +void free_config(struct daemon_conf *config) +{ + free((void*)config->sender_ctx); + free((void*)config->log_file); + free((void*)config->dispatcher); + free((void *)config->node_name); + free((void *)config->action_mail_acct); + free((void *)config->space_left_exe); + free((void *)config->admin_space_left_exe); + free((void *)config->disk_full_exe); + free((void *)config->disk_error_exe); +} + diff --git a/framework/src/audit/auparse/auparse-defs.h b/framework/src/audit/auparse/auparse-defs.h new file mode 100644 index 00000000..fd7ed85d --- /dev/null +++ b/framework/src/audit/auparse/auparse-defs.h @@ -0,0 +1,98 @@ +/* auparse-defs.h -- + * Copyright 2006-07,09,2011-12,2014-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#ifndef AUPARSE_DEFS_HEADER +#define AUPARSE_DEFS_HEADER + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Library type definitions */ + +/* This tells the library where the data source is located */ +typedef enum { AUSOURCE_LOGS, AUSOURCE_FILE, AUSOURCE_FILE_ARRAY, + AUSOURCE_BUFFER, AUSOURCE_BUFFER_ARRAY, + AUSOURCE_DESCRIPTOR, AUSOURCE_FILE_POINTER, AUSOURCE_FEED } ausource_t; + +/* This used to define the types of searches that can be done. It is not used + any more. */ +typedef enum { + AUSEARCH_UNSET, + AUSEARCH_EXISTS, + AUSEARCH_EQUAL, AUSEARCH_NOT_EQUAL, + AUSEARCH_TIME_LT, AUSEARCH_TIME_LE, AUSEARCH_TIME_GE, AUSEARCH_TIME_GT, + AUSEARCH_TIME_EQ, + AUSEARCH_INTERPRETED = 0x40000000 +} ausearch_op_t; + +/* This determines where to position the cursor when a search completes */ +typedef enum { AUSEARCH_STOP_EVENT, AUSEARCH_STOP_RECORD, + AUSEARCH_STOP_FIELD } austop_t; + +/* This defines how search rule pieces are treated to decide when + * to stop a search */ +typedef enum { AUSEARCH_RULE_CLEAR, AUSEARCH_RULE_OR, + AUSEARCH_RULE_AND, AUSEARCH_RULE_REGEX } ausearch_rule_t; + + +typedef struct +{ + time_t sec; // Event seconds + unsigned int milli; // millisecond of the timestamp + unsigned long serial; // Serial number of the event + const char *host; // Machine's name +} au_event_t; + + +/* This indicates why the user supplied callback was invoked */ +typedef enum {AUPARSE_CB_EVENT_READY} auparse_cb_event_t; + +/* This determines the type of field at current cursor location + * ONLY APPEND - DO NOT DELETE or it will break ABI */ +typedef enum { AUPARSE_TYPE_UNCLASSIFIED, AUPARSE_TYPE_UID, AUPARSE_TYPE_GID, + AUPARSE_TYPE_SYSCALL, AUPARSE_TYPE_ARCH, AUPARSE_TYPE_EXIT, + AUPARSE_TYPE_ESCAPED, AUPARSE_TYPE_PERM, AUPARSE_TYPE_MODE, + AUPARSE_TYPE_SOCKADDR, AUPARSE_TYPE_FLAGS, AUPARSE_TYPE_PROMISC, + AUPARSE_TYPE_CAPABILITY, AUPARSE_TYPE_SUCCESS, AUPARSE_TYPE_A0, + AUPARSE_TYPE_A1, AUPARSE_TYPE_A2, AUPARSE_TYPE_A3, AUPARSE_TYPE_SIGNAL, + AUPARSE_TYPE_LIST, AUPARSE_TYPE_TTY_DATA, + AUPARSE_TYPE_SESSION, AUPARSE_TYPE_CAP_BITMAP, AUPARSE_TYPE_NFPROTO, + AUPARSE_TYPE_ICMPTYPE, AUPARSE_TYPE_PROTOCOL, + AUPARSE_TYPE_ADDR, AUPARSE_TYPE_PERSONALITY, + AUPARSE_TYPE_SECCOMP, AUPARSE_TYPE_OFLAG, + AUPARSE_TYPE_MMAP, AUPARSE_TYPE_MODE_SHORT, AUPARSE_TYPE_MAC_LABEL, + AUPARSE_TYPE_PROCTITLE } auparse_type_t; + +/* This type determines what escaping if any gets applied to interpreted fields */ +typedef enum { AUPARSE_ESC_RAW, AUPARSE_ESC_TTY, AUPARSE_ESC_SHELL, + AUPARSE_ESC_SHELL_QUOTE } auparse_esc_t; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/auparse/auparse-idata.h b/framework/src/audit/auparse/auparse-idata.h new file mode 100644 index 00000000..d1995538 --- /dev/null +++ b/framework/src/audit/auparse/auparse-idata.h @@ -0,0 +1,49 @@ +/* +* idata.h - Header file for ausearch-lookup.c +* Copyright (c) 2013 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#ifndef IDATA_HEADER +#define IDATA_HEADER + +#include "config.h" +#include "dso.h" +#include "auparse-defs.h" + +typedef struct _idata { + unsigned int machine; // The machine type for the event + int syscall; // The syscall for the event + unsigned long long a0; // arg 0 to the syscall + unsigned long long a1; // arg 1 to the syscall + const char *name; // name of field being interpretted + const char *val; // value of field being interpretted +} idata; + +int auparse_interp_adjust_type(int rtype, const char *name, const char *val); +const char *auparse_do_interpretation(int type, const idata *id); +int set_escape_mode(auparse_esc_t mode); + +hidden_proto(auparse_interp_adjust_type) +hidden_proto(auparse_do_interpretation) +hidden_proto(set_escape_mode) + +#endif + diff --git a/framework/src/audit/auparse/auparse.c b/framework/src/audit/auparse/auparse.c new file mode 100644 index 00000000..cd3f1180 --- /dev/null +++ b/framework/src/audit/auparse/auparse.c @@ -0,0 +1,1377 @@ +/* auparse.c -- + * Copyright 2006-08,2012-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include "expression.h" +#include "internal.h" +#include "auparse.h" +#include "interpret.h" +#include "auparse-idata.h" +#include +#include +#include +#include +#include + +static int debug = 0; + +/* like strchr except string is delimited by length, not null byte */ +static char *strnchr(const char *s, int c, size_t n) +{ + char *p_char; + const char *p_end = s + n; + + for (p_char = (char *)s; p_char < p_end && *p_char != c; p_char++); + if (p_char == p_end) return NULL; + return p_char; +} + +static int setup_log_file_array(auparse_state_t *au) +{ + struct daemon_conf config; + char *filename, **tmp; + int len, num = 0, i = 0; + + /* Load config so we know where logs are */ + set_aumessage_mode(MSG_STDERR, DBG_NO); + load_config(&config, TEST_SEARCH); + + /* for each file */ + len = strlen(config.log_file) + 16; + filename = malloc(len); + if (!filename) { + fprintf(stderr, "No memory\n"); + free_config(&config); + return 1; + } + /* Find oldest log file */ + snprintf(filename, len, "%s", config.log_file); + do { + if (access(filename, R_OK) != 0) + break; + num++; + snprintf(filename, len, "%s.%d", config.log_file, num); + } while (1); + + if (num == 0) { + fprintf(stderr, "No log file\n"); + free_config(&config); + free(filename); + return 1; + } + num--; + tmp = malloc((num+2)*sizeof(char *)); + + /* Got it, now process logs from last to first */ + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else + snprintf(filename, len, "%s", config.log_file); + do { + tmp[i++] = strdup(filename); + + /* Get next log file */ + num--; + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else if (num == 0) + snprintf(filename, len, "%s", config.log_file); + else + break; + } while (1); + free_config(&config); + free(filename); + + // Terminate the list + tmp[i] = NULL; + au->source_list = tmp; + return 0; +} + +/* General functions that affect operation of the library */ +auparse_state_t *auparse_init(ausource_t source, const void *b) +{ + char **tmp, **bb = (char **)b, *buf = (char *)b; + int n, i; + size_t size, len; + + auparse_state_t *au = malloc(sizeof(auparse_state_t)); + if (au == NULL) { + errno = ENOMEM; + return NULL; + } + + au->in = NULL; + au->source_list = NULL; + databuf_init(&au->databuf, 0, 0); + au->callback = NULL; + au->callback_user_data = NULL; + au->callback_user_data_destroy = NULL; + switch (source) + { + case AUSOURCE_LOGS: + if (geteuid()) { + errno = EPERM; + goto bad_exit; + } + setup_log_file_array(au); + break; + case AUSOURCE_FILE: + if (access(b, R_OK)) + goto bad_exit; + tmp = malloc(2*sizeof(char *)); + tmp[0] = strdup(b); + tmp[1] = NULL; + au->source_list = tmp; + break; + case AUSOURCE_FILE_ARRAY: + n = 0; + while (bb[n]) { + if (access(bb[n], R_OK)) + goto bad_exit; + n++; + } + tmp = malloc((n+1)*sizeof(char *)); + for (i=0; isource_list = tmp; + break; + case AUSOURCE_BUFFER: + buf = buf; + len = strlen(buf); + if (databuf_init(&au->databuf, len, + DATABUF_FLAG_PRESERVE_HEAD) < 0) + goto bad_exit; + if (databuf_append(&au->databuf, buf, len) < 0) + goto bad_exit; + break; + case AUSOURCE_BUFFER_ARRAY: + size = 0; + for (n = 0; (buf = bb[n]); n++) { + len = strlen(bb[n]); + if (bb[n][len-1] != '\n') { + size += len + 1; + } else { + size += len; + } + } + if (databuf_init(&au->databuf, size, + DATABUF_FLAG_PRESERVE_HEAD) < 0) + goto bad_exit; + for (n = 0; (buf = bb[n]); n++) { + len = strlen(buf); + if (databuf_append(&au->databuf, buf, len) < 0) + goto bad_exit; + } + break; + case AUSOURCE_DESCRIPTOR: + n = (long)b; + au->in = fdopen(n, "rm"); + break; + case AUSOURCE_FILE_POINTER: + au->in = (FILE *)b; + break; + case AUSOURCE_FEED: + if (databuf_init(&au->databuf, 0, 0) < 0) goto bad_exit; + break; + default: + errno = EINVAL; + goto bad_exit; + break; + } + au->source = source; + au->list_idx = 0; + au->line_number = 0; + au->next_buf = NULL; + au->off = 0; + au->cur_buf = NULL; + au->line_pushed = 0; + aup_list_create(&au->le); + au->parse_state = EVENT_EMPTY; + au->expr = NULL; + au->find_field = NULL; + au->search_where = AUSEARCH_STOP_EVENT; + + return au; +bad_exit: + databuf_free(&au->databuf); + free(au); + return NULL; +} + + +void auparse_add_callback(auparse_state_t *au, auparse_callback_ptr callback, + void *user_data, user_destroy user_destroy_func) +{ + if (au == NULL) { + errno = EINVAL; + return; + } + + if (au->callback_user_data_destroy) { + (*au->callback_user_data_destroy)(au->callback_user_data); + au->callback_user_data = NULL; + } + + au->callback = callback; + au->callback_user_data = user_data; + au->callback_user_data_destroy = user_destroy_func; +} + +static void consume_feed(auparse_state_t *au, int flush) +{ + while (auparse_next_event(au) > 0) { + if (au->callback) { + (*au->callback)(au, AUPARSE_CB_EVENT_READY, + au->callback_user_data); + } + } + if (flush) { + // FIXME: might need a call here to force auparse_next_event() + // to consume any partial data not fully consumed. + if (au->parse_state == EVENT_ACCUMULATING) { + // Emit the event, set event cursors to initial position + aup_list_first(&au->le); + aup_list_first_field(&au->le); + au->parse_state = EVENT_EMITTED; + if (au->callback) { + (*au->callback)(au, AUPARSE_CB_EVENT_READY, + au->callback_user_data); + } + } + } +} + +int auparse_feed(auparse_state_t *au, const char *data, size_t data_len) +{ + if (databuf_append(&au->databuf, data, data_len) < 0) + return -1; + consume_feed(au, 0); + return 0; +} + +int auparse_flush_feed(auparse_state_t *au) +{ + consume_feed(au, 1); + return 0; +} + +// If there is data in the state machine, return 1 +// Otherwise return 0 to indicate its empty +int auparse_feed_has_data(const auparse_state_t *au) +{ + if (au->parse_state == EVENT_ACCUMULATING) + return 1; + return 0; +} + +void auparse_set_escape_mode(auparse_esc_t mode) +{ + set_escape_mode(mode); +} + +int auparse_reset(auparse_state_t *au) +{ + if (au == NULL) { + errno = EINVAL; + return -1; + } + + aup_list_clear(&au->le); + au->parse_state = EVENT_EMPTY; + switch (au->source) + { + case AUSOURCE_LOGS: + case AUSOURCE_FILE: + case AUSOURCE_FILE_ARRAY: + if (au->in) { + fclose(au->in); + au->in = NULL; + } + /* Fall through */ + case AUSOURCE_DESCRIPTOR: + case AUSOURCE_FILE_POINTER: + if (au->in) + rewind(au->in); + /* Fall through */ + case AUSOURCE_BUFFER: + case AUSOURCE_BUFFER_ARRAY: + au->list_idx = 0; + au->line_number = 0; + au->off = 0; + databuf_reset(&au->databuf); + break; + default: + return -1; + } + return 0; +} + + +/* Add EXPR to AU, using HOW to select the combining operator. + On success, return 0. + On error, free EXPR set errno and return -1. + NOTE: EXPR is freed on error! */ +static int add_expr(auparse_state_t *au, struct expr *expr, ausearch_rule_t how) +{ + if (au->expr == NULL) + au->expr = expr; + else if (how == AUSEARCH_RULE_CLEAR) { + expr_free(au->expr); + au->expr = expr; + } else { + struct expr *e; + + e = expr_create_binary(how == AUSEARCH_RULE_OR ? EO_OR : EO_AND, + au->expr, expr); + if (e == NULL) { + int err; + + err = errno; + expr_free(expr); + errno = err; + return -1; + } + au->expr = e; + } + return 0; +} + +static int ausearch_add_item_internal(auparse_state_t *au, const char *field, + const char *op, const char *value, ausearch_rule_t how, unsigned op_eq, + unsigned op_ne) +{ + struct expr *expr; + + // Make sure there's a field + if (field == NULL) + goto err_out; + + // Make sure how is within range + if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) + goto err_out; + + // All pre-checks are done, build a rule + if (strcmp(op, "exists") == 0) + expr = expr_create_field_exists(field); + else { + unsigned t_op; + + if (strcmp(op, "=") == 0) + t_op = op_eq; + else if (strcmp(op, "!=") == 0) + t_op = op_ne; + else + goto err_out; + if (value == NULL) + goto err_out; + expr = expr_create_comparison(field, t_op, value); + } + if (expr == NULL) + return -1; + if (add_expr(au, expr, how) != 0) + return -1; /* expr is freed by add_expr() */ + return 0; + +err_out: + errno = EINVAL; + return -1; +} + +int ausearch_add_item(auparse_state_t *au, const char *field, const char *op, + const char *value, ausearch_rule_t how) +{ + return ausearch_add_item_internal(au, field, op, value, how, EO_RAW_EQ, + EO_RAW_NE); +} + +int ausearch_add_interpreted_item(auparse_state_t *au, const char *field, + const char *op, const char *value, ausearch_rule_t how) +{ + return ausearch_add_item_internal(au, field, op, value, how, + EO_INTERPRETED_EQ, EO_INTERPRETED_NE); +} + +int ausearch_add_timestamp_item_ex(auparse_state_t *au, const char *op, + time_t sec, unsigned milli, unsigned serial, ausearch_rule_t how) +{ + static const struct { + unsigned value; + const char name[3]; + } ts_tab[] = { + {EO_VALUE_LT, "<"}, + {EO_VALUE_LE, "<="}, + {EO_VALUE_GE, ">="}, + {EO_VALUE_GT, ">"}, + {EO_VALUE_EQ, "="}, + }; + + struct expr *expr; + size_t i; + unsigned t_op; + + for (i = 0; i < sizeof(ts_tab) / sizeof(*ts_tab); i++) { + if (strcmp(ts_tab[i].name, op) == 0) + goto found_op; + } + goto err_out; +found_op: + t_op = ts_tab[i].value; + + if (milli >= 1000) + goto err_out; + + // Make sure how is within range + if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) + goto err_out; + + // All pre-checks are done, build a rule + expr = expr_create_timestamp_comparison_ex(t_op, sec, milli, serial); + if (expr == NULL) + return -1; + if (add_expr(au, expr, how) != 0) + return -1; /* expr is freed by add_expr() */ + return 0; + +err_out: + errno = EINVAL; + return -1; +} + +int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec, + unsigned milli, ausearch_rule_t how) +{ + return ausearch_add_timestamp_item_ex(au, op, sec, milli, 0, how); +} + +int ausearch_add_expression(auparse_state_t *au, const char *expression, + char **error, ausearch_rule_t how) +{ + struct expr *expr; + + if (how < AUSEARCH_RULE_CLEAR || how > AUSEARCH_RULE_AND) + goto err_einval; + + expr = expr_parse(expression, error); + if (expr == NULL) { + errno = EINVAL; + return -1; + } + + if (add_expr(au, expr, how) != 0) + goto err; /* expr is freed by add_expr() */ + return 0; + +err_einval: + errno = EINVAL; +err: + *error = NULL; + return -1; +} + +int ausearch_add_regex(auparse_state_t *au, const char *regexp) +{ + struct expr *expr; + + // Make sure there's an expression + if (regexp == NULL) + goto err_out; + + expr = expr_create_regexp_expression(regexp); + if (expr == NULL) + return -1; + if (add_expr(au, expr, AUSEARCH_RULE_AND) != 0) + return -1; /* expr is freed by add_expr() */ + return 0; + +err_out: + errno = EINVAL; + return -1; +} + +int ausearch_set_stop(auparse_state_t *au, austop_t where) +{ + if (where < AUSEARCH_STOP_EVENT || where > AUSEARCH_STOP_FIELD) { + errno = EINVAL; + return -1; + } + + au->search_where = where; + return 0; +} + +void ausearch_clear(auparse_state_t *au) +{ + if (au->expr != NULL) { + expr_free(au->expr); + au->expr = NULL; + } + au->search_where = AUSEARCH_STOP_EVENT; +} + +void auparse_destroy(auparse_state_t *au) +{ + aulookup_destroy_uid_list(); + aulookup_destroy_gid_list(); + if (au == NULL) + return; + + if (au->source_list) { + int n = 0; + while (au->source_list[n]) + free(au->source_list[n++]); + free(au->source_list); + au->source_list = NULL; + } + + au->next_buf = NULL; + free(au->cur_buf); + au->cur_buf = NULL; + aup_list_clear(&au->le); + au->parse_state = EVENT_EMPTY; + free(au->find_field); + au->find_field = NULL; + ausearch_clear(au); + databuf_free(&au->databuf); + if (au->callback_user_data_destroy) { + (*au->callback_user_data_destroy)(au->callback_user_data); + au->callback_user_data = NULL; + } + if (au->in) { + fclose(au->in); + au->in = NULL; + } + free(au); +} + +/* alloc a new buffer, cur_buf which contains a null terminated line + * without a newline (note, this implies the line may be empty (strlen == 0)) if + * successfully read a blank line (e.g. containing only a single newline). + * cur_buf will have been newly allocated with malloc. + * + * Note: cur_buf will be freed the next time this routine is called if + * cur_buf is not NULL, callers who retain a reference to the cur_buf + * pointer will need to set cur_buf to NULL to cause the previous cur_buf + * allocation to persist. + * + * Returns: + * 1 if successful (errno == 0) + * 0 if non-blocking input unavailable (errno == 0) + * -1 if error (errno contains non-zero error code) + * -2 if EOF (errno == 0) + */ + +static int readline_file(auparse_state_t *au) +{ + ssize_t rc; + char *p_last_char; + size_t n = 0; + + if (au->cur_buf != NULL) { + free(au->cur_buf); + au->cur_buf = NULL; + } + if (au->in == NULL) { + errno = EBADF; + return -1; + } + if ((rc = getline(&au->cur_buf, &n, au->in)) <= 0) { + // Note: getline always malloc's if lineptr==NULL or n==0, + // on failure malloc'ed memory is left uninitialized, + // caller must free it. + free(au->cur_buf); + au->cur_buf = NULL; + + // Note: feof() does not set errno + if (feof(au->in)) { + // return EOF condition + errno = 0; + return -2; + } + // return error condition, error code in errno + return -1; + } + p_last_char = au->cur_buf + (rc-1); + if (*p_last_char == '\n') { /* nuke newline */ + *p_last_char = 0; + } + // return success + errno = 0; + return 1; +} + + +/* malloc & copy a line into cur_buf from the internal buffer, + * next_buf. cur_buf will contain a null terminated line without a + * newline (note, this implies the line may be empty (strlen == 0)) if + * successfully read a blank line (e.g. containing only a single + * newline). + * + * Note: cur_buf will be freed the next time this routine is called if + * cur_buf is not NULL, callers who retain a reference to the cur_buf + * pointer will need to set cur_buf to NULL to cause the previous cur_buf + * allocation to persist. + * + * Returns: + * 1 if successful (errno == 0) + * 0 if non-blocking input unavailable (errno == 0) + * -1 if error (errno contains non-zero error code) + * -2 if EOF (errno == 0) + */ + +static int readline_buf(auparse_state_t *au) +{ + char *p_newline=NULL; + size_t line_len; + + if (au->cur_buf != NULL) { + free(au->cur_buf); + au->cur_buf = NULL; + } + + //if (debug) databuf_print(&au->databuf, 1, "readline_buf"); + if (au->databuf.len == 0) { + // return EOF condition + errno = 0; + return -2; + } + + if ((p_newline = strnchr(databuf_beg(&au->databuf), '\n', + au->databuf.len)) != NULL) { + line_len = p_newline - databuf_beg(&au->databuf); + + /* dup the line */ + au->cur_buf = malloc(line_len+1); // +1 for null terminator + if (au->cur_buf == NULL) + return -1; // return error condition, errno set + strncpy(au->cur_buf, databuf_beg(&au->databuf), line_len); + au->cur_buf[line_len] = 0; + + if (databuf_advance(&au->databuf, line_len+1) < 0) + return -1; + // return success + errno = 0; + return 1; + + } else { + // return no data available + errno = 0; + return 0; + } +} + +static int str2event(char *s, au_event_t *e) +{ + char *ptr; + + errno = 0; + ptr = strchr(s+10, ':'); + if (ptr) { + e->serial = strtoul(ptr+1, NULL, 10); + *ptr = 0; + if (errno) + return -1; + } else + e->serial = 0; + ptr = strchr(s, '.'); + if (ptr) { + e->milli = strtoul(ptr+1, NULL, 10); + *ptr = 0; + if (errno) + return -1; + } else + e->milli = 0; + e->sec = strtoul(s, NULL, 10); + if (errno) + return -1; + return 0; +} + +/* Returns 0 on success and 1 on error */ +static int extract_timestamp(const char *b, au_event_t *e) +{ + char *ptr, *tmp; + int rc = 1; + + e->host = NULL; + if (*b == 'n') + tmp = strndupa(b, 340); + else + tmp = strndupa(b, 80); + ptr = audit_strsplit(tmp); + if (ptr) { + // Optionally grab the node - may or may not be included + if (*ptr == 'n') { + e->host = strdup(ptr+5); + (void)audit_strsplit(NULL); // Bump along to the next one + } + // at this point we have type= + ptr = audit_strsplit(NULL); + if (ptr) { + if (*(ptr+9) == '(') + ptr+=9; + else + ptr = strchr(ptr, '('); + if (ptr) { + // now we should be pointed at the timestamp + char *eptr; + ptr++; + eptr = strchr(ptr, ')'); + if (eptr) + *eptr = 0; + + if (str2event(ptr, e) == 0) + rc = 0; +// else { +// audit_msg(LOG_ERROR, +// "Error extracting time stamp (%s)\n", +// ptr); +// } + } + // else we have a bad line + } + // else we have a bad line + } + // else we have a bad line + return rc; +} + +static int inline events_are_equal(au_event_t *e1, au_event_t *e2) +{ + // Check time & serial first since its most likely way + // to spot 2 different events + if (!(e1->serial == e2->serial && e1->milli == e2->milli && + e1->sec == e2->sec)) + return 0; + // Hmm...same so far, check if both have a host, only a string + // compare can tell if they are the same. Otherwise, if only one + // of them have a host, they are definitely not the same. Its + // a boundary on daemon config. + if (e1->host && e2->host) { + if (strcmp(e1->host, e2->host)) + return 0; + } else if (e1->host || e2->host) + return 0; + return 1; +} + +/* This function will figure out how to get the next line of input. + * storing it cur_buf. cur_buf will be NULL terminated but will not + * contain a trailing newline. This implies a successful read + * (result == 1) may result in a zero length cur_buf if a blank line + * was read. + * + * cur_buf will have been allocated with malloc. The next time this + * routine is called if cur_buf is non-NULL cur_buf will be freed, + * thus if the caller wishes to retain a reference to malloc'ed + * cur_buf data it should copy the cur_buf pointer and set cur_buf to + * NULL. + * + * Returns: + * 1 if successful (errno == 0) + * 0 if non-blocking input unavailable (errno == 0) + * -1 if error (errno contains non-zero error code) + * -2 if EOF (errno == 0) + */ + +static int retrieve_next_line(auparse_state_t *au) +{ + int rc; + + // If line was pushed back for re-reading return that + if (au->line_pushed) { + // Starting new event, clear previous event data, + // previous line is returned again for new parsing + au->line_pushed = 0; + au->line_number++; + return 1; + } + + switch (au->source) + { + case AUSOURCE_DESCRIPTOR: + case AUSOURCE_FILE_POINTER: + rc = readline_file(au); + if (rc > 0) au->line_number++; + return rc; + case AUSOURCE_LOGS: + case AUSOURCE_FILE: + case AUSOURCE_FILE_ARRAY: + // if the first time through, open file + if (au->list_idx == 0 && au->in == NULL && + au->source_list != NULL) { + if (au->source_list[au->list_idx] == NULL) { + errno = 0; + return -2; + } + au->line_number = 0; + au->in = fopen(au->source_list[au->list_idx], + "rm"); + if (au->in == NULL) + return -1; + __fsetlocking(au->in, FSETLOCKING_BYCALLER); + } + + // loop reading lines from a file + while (au->in) { + if ((rc = readline_file(au)) == -2) { + // end of file, open next file, + // try readline again + fclose(au->in); + au->in = NULL; + au->list_idx++; + au->line_number = 0; + if (au->source_list[au->list_idx]) { + au->in = fopen( + au->source_list[au->list_idx], + "rm"); + if (au->in == NULL) + return -1; + __fsetlocking(au->in, + FSETLOCKING_BYCALLER); + } + } else { + if (rc > 0) + au->line_number++; + return rc; + } + } + return -2; // return EOF + case AUSOURCE_BUFFER: + case AUSOURCE_BUFFER_ARRAY: + rc = readline_buf(au); + if (rc > 0) + au->line_number++; + return rc; + case AUSOURCE_FEED: + rc = readline_buf(au); + // No such thing as EOF for feed, translate EOF + // to data not available + if (rc == -2) + return 0; + else + if (rc > 0) + au->line_number++; + return rc; + default: + return -1; + } + return -1; /* should never reach here */ +} + +static void push_line(auparse_state_t *au) +{ + au->line_number--; + au->line_pushed = 1; +} + +/******* +* Functions that traverse events. +********/ +static int ausearch_reposition_cursors(auparse_state_t *au) +{ + int rc = 0; + + switch (au->search_where) + { + case AUSEARCH_STOP_EVENT: + aup_list_first(&au->le); + aup_list_first_field(&au->le); + break; + case AUSEARCH_STOP_RECORD: + aup_list_first_field(&au->le); + break; + case AUSEARCH_STOP_FIELD: + // do nothing - this is the normal stopping point + break; + default: + rc = -1; + break; + } + return rc; +} + +/* This is called during search once per each record. It walks the list + * of nvpairs and decides if a field matches. */ +static int ausearch_compare(auparse_state_t *au) +{ + rnode *r; + + r = aup_list_get_cur(&au->le); + if (r) + return expr_eval(au, r, au->expr); + + return 0; +} + +// Returns < 0 on error, 0 no data, > 0 success +int ausearch_next_event(auparse_state_t *au) +{ + int rc; + + if (au->expr == NULL) { + errno = EINVAL; + return -1; + } + if ((rc = auparse_first_record(au)) <= 0) + return rc; + do { + do { + if ((rc = ausearch_compare(au)) > 0) { + ausearch_reposition_cursors(au); + return 1; + } else if (rc < 0) + return rc; + } while ((rc = auparse_next_record(au)) > 0); + if (rc < 0) + return rc; + } while ((rc = auparse_next_event(au)) > 0); + if (rc < 0) + return rc; + + return 0; +} + +// Brute force go to next event. Returns < 0 on error, 0 no data, > 0 success +int auparse_next_event(auparse_state_t *au) +{ + int rc; + au_event_t event; + + if (au->parse_state == EVENT_EMITTED) { + // If the last call resulted in emitting event data then + // clear previous event data in preparation to accumulate + // new event data + aup_list_clear(&au->le); + au->parse_state = EVENT_EMPTY; + } + + // accumulate new event data + while (1) { + rc = retrieve_next_line(au); + if (debug) printf("next_line(%d) '%s'\n", rc, au->cur_buf); + if (rc == 0) return 0; // No data now + if (rc == -2) { + // We're at EOF, did we read any data previously? + // If so return data available, else return no data + // available + if (au->parse_state == EVENT_ACCUMULATING) { + if (debug) printf("EOF, EVENT_EMITTED\n"); + au->parse_state = EVENT_EMITTED; + return 1; // data is available + } + return 0; + } + if (rc > 0) { // Input available + rnode *r; + if (extract_timestamp(au->cur_buf, &event)) { + if (debug) + printf("Malformed line:%s\n", + au->cur_buf); + continue; + } + if (au->parse_state == EVENT_EMPTY) { + // First record in new event, initialize event + if (debug) + printf( + "First record in new event, initialize event\n"); + aup_list_set_event(&au->le, &event); + aup_list_append(&au->le, au->cur_buf, + au->list_idx, au->line_number); + au->parse_state = EVENT_ACCUMULATING; + au->cur_buf = NULL; + } else if (events_are_equal(&au->le.e, &event)) { + // Accumulate data into existing event + if (debug) + printf( + "Accumulate data into existing event\n"); + aup_list_append(&au->le, au->cur_buf, + au->list_idx, au->line_number); + au->parse_state = EVENT_ACCUMULATING; + au->cur_buf = NULL; + } else { + // New event, save input for next invocation + if (debug) + printf( + "New event, save current input for next invocation, EVENT_EMITTED\n"); + push_line(au); + // Emit the event, set event cursors to + // initial position + aup_list_first(&au->le); + aup_list_first_field(&au->le); + au->parse_state = EVENT_EMITTED; + free((char *)event.host); + return 1; // data is available + } + free((char *)event.host); + // Check to see if the event can be emitted due to EOE + // or something we know is a single record event. At + // this point, new record should be pointed at 'cur' + if ((r = aup_list_get_cur(&au->le)) == NULL) + continue; + if ( r->type == AUDIT_EOE || + r->type < AUDIT_FIRST_EVENT || + r->type >= AUDIT_FIRST_ANOM_MSG) { + // Emit the event, set event cursors to + // initial position + aup_list_first(&au->le); + aup_list_first_field(&au->le); + au->parse_state = EVENT_EMITTED; + return 1; // data is available + } + } else { // Read error + return -1; + } + } +} + +/* Accessors to event data */ +const au_event_t *auparse_get_timestamp(auparse_state_t *au) +{ + if (au && au->le.e.sec != 0) + return &au->le.e; + else + return NULL; +} + + +time_t auparse_get_time(auparse_state_t *au) +{ + if (au) + return au->le.e.sec; + else + return 0; +} + + +unsigned int auparse_get_milli(auparse_state_t *au) +{ + if (au) + return au->le.e.milli; + else + return 0; +} + + +unsigned long auparse_get_serial(auparse_state_t *au) +{ + if (au) + return au->le.e.serial; + else + return 0; +} + + +// Gets the machine node name +const char *auparse_get_node(auparse_state_t *au) +{ + if (au && au->le.e.host != NULL) + return strdup(au->le.e.host); + else + return NULL; +} + + +int auparse_node_compare(au_event_t *e1, au_event_t *e2) +{ + // If both have a host, only a string compare can tell if they + // are the same. Otherwise, if only one of them have a host, they + // are definitely not the same. Its a boundary on daemon config. + if (e1->host && e2->host) + return strcmp(e1->host, e2->host); + else if (e1->host) + return 1; + else if (e2->host) + return -1; + + return 0; +} + + +int auparse_timestamp_compare(au_event_t *e1, au_event_t *e2) +{ + if (e1->sec > e2->sec) + return 1; + if (e1->sec < e2->sec) + return -1; + + if (e1->milli > e2->milli) + return 1; + if (e1->milli < e2->milli) + return -1; + + if (e1->serial > e2->serial) + return 1; + if (e1->serial < e2->serial) + return -1; + + return 0; +} + +unsigned int auparse_get_num_records(auparse_state_t *au) +{ + return aup_list_get_cnt(&au->le); +} + + +/* Functions that traverse records in the same event */ +int auparse_first_record(auparse_state_t *au) +{ + int rc; + + if (aup_list_get_cnt(&au->le) == 0) { + rc = auparse_next_event(au); + if (rc <= 0) + return rc; + } + aup_list_first(&au->le); + aup_list_first_field(&au->le); + + return 1; +} + + +int auparse_next_record(auparse_state_t *au) +{ + if (aup_list_get_cnt(&au->le) == 0) { + int rc = auparse_first_record(au); + if (rc <= 0) + return rc; + } + if (aup_list_next(&au->le)) + return 1; + else + return 0; +} + + +int auparse_goto_record_num(auparse_state_t *au, unsigned int num) +{ + /* Check if a request is out of range */ + if (num >= aup_list_get_cnt(&au->le)) + return 0; + + if (aup_list_goto_rec(&au->le, num) != NULL) + return 1; + else + return 0; +} + + +/* Accessors to record data */ +int auparse_get_type(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return r->type; + else + return 0; +} + + +const char *auparse_get_type_name(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return audit_msg_type_to_name(r->type); + else + return NULL; +} + + +unsigned int auparse_get_line_number(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return r->line_number; + else + return 0; +} + + +const char *auparse_get_filename(auparse_state_t *au) +{ + switch (au->source) + { + case AUSOURCE_FILE: + case AUSOURCE_FILE_ARRAY: + break; + default: + return NULL; + } + + rnode *r = aup_list_get_cur(&au->le); + if (r) { + if (r->list_idx < 0) return NULL; + return au->source_list[r->list_idx]; + } else { + return NULL; + } +} + + +int auparse_first_field(auparse_state_t *au) +{ + return aup_list_first_field(&au->le); +} + + +int auparse_next_field(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) { + if (nvlist_next(&r->nv)) + return 1; + else + return 0; + } + return 0; +} + + +unsigned int auparse_get_num_fields(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cnt(&r->nv); + else + return 0; +} + +const char *auparse_get_record_text(auparse_state_t *au) +{ + rnode *r = aup_list_get_cur(&au->le); + if (r) + return r->record; + else + return NULL; +} + + +/* scan from current location to end of event */ +const char *auparse_find_field(auparse_state_t *au, const char *name) +{ + free(au->find_field); + au->find_field = strdup(name); + + if (au->le.e.sec) { + const char *cur_name; + rnode *r; + + // look at current record before moving + r = aup_list_get_cur(&au->le); + if (r == NULL) + return NULL; + cur_name = nvlist_get_cur_name(&r->nv); + if (cur_name && strcmp(cur_name, name) == 0) + return nvlist_get_cur_val(&r->nv); + + return auparse_find_field_next(au); + } + return NULL; +} + +/* Increment 1 location and then scan for next field */ +const char *auparse_find_field_next(auparse_state_t *au) +{ + if (au->find_field == NULL) { + errno = EINVAL; + return NULL; + } + if (au->le.e.sec) { + int moved = 0; + + rnode *r = aup_list_get_cur(&au->le); + while (r) { // For each record in the event... + if (!moved) { + nvlist_next(&r->nv); + moved=1; + } + if (nvlist_find_name(&r->nv, au->find_field)) + return nvlist_get_cur_val(&r->nv); + r = aup_list_next(&au->le); + if (r) + aup_list_first_field(&au->le); + } + } + return NULL; +} + + +/* Accessors to field data */ +const char *auparse_get_field_name(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cur_name(&r->nv); + } + return NULL; +} + + +const char *auparse_get_field_str(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cur_val(&r->nv); + } + return NULL; +} + +int auparse_get_field_type(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_get_cur_type(r); + } + return AUPARSE_TYPE_UNCLASSIFIED; +} + +int auparse_get_field_int(auparse_state_t *au) +{ + const char *v = auparse_get_field_str(au); + if (v) { + int val; + + errno = 0; + val = strtol(v, NULL, 10); + if (errno == 0) + return val; + } else + errno = ENODATA; + return -1; +} + +const char *auparse_interpret_field(auparse_state_t *au) +{ + if (au->le.e.sec) { + rnode *r = aup_list_get_cur(&au->le); + if (r) + return nvlist_interp_cur_val(r); + } + return NULL; +} + diff --git a/framework/src/audit/auparse/auparse.h b/framework/src/audit/auparse/auparse.h new file mode 100644 index 00000000..78504ffe --- /dev/null +++ b/framework/src/audit/auparse/auparse.h @@ -0,0 +1,112 @@ +/* auparse.h -- + * Copyright 2006-08,2012,2014,2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#ifndef AUPARSE_HEADER +#define AUPARSE_HEADER + +#include "auparse-defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Library type definitions */ + +/* opaque data type used for maintaining library state */ +typedef struct opaque auparse_state_t; + +typedef void (*user_destroy)(void *user_data); +typedef void (*auparse_callback_ptr)(auparse_state_t *au, + auparse_cb_event_t cb_event_type, void *user_data); + +/* General functions that affect operation of the library */ +auparse_state_t *auparse_init(ausource_t source, const void *b); +int auparse_feed(auparse_state_t *au, const char *data, size_t data_len); +int auparse_flush_feed(auparse_state_t *au); +int auparse_feed_has_data(const auparse_state_t *au); +void auparse_add_callback(auparse_state_t *au, auparse_callback_ptr callback, + void *user_data, user_destroy user_destroy_func); +void auparse_set_escape_mode(auparse_esc_t mode); +int auparse_reset(auparse_state_t *au); +void auparse_destroy(auparse_state_t *au); + +/* Functions that are part of the search interface */ +int ausearch_add_expression(auparse_state_t *au, const char *expression, + char **error, ausearch_rule_t how); +int ausearch_add_item(auparse_state_t *au, const char *field, const char *op, + const char *value, ausearch_rule_t how); +int ausearch_add_interpreted_item(auparse_state_t *au, const char *field, + const char *op, const char *value, ausearch_rule_t how); +int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec, + unsigned milli, ausearch_rule_t how); +int ausearch_add_timestamp_item_ex(auparse_state_t *au, const char *op, + time_t sec, unsigned milli, unsigned serial, ausearch_rule_t how); +int ausearch_add_regex(auparse_state_t *au, const char *expr); +int ausearch_set_stop(auparse_state_t *au, austop_t where); +void ausearch_clear(auparse_state_t *au); + +/* Functions that traverse events */ +int ausearch_next_event(auparse_state_t *au); +int auparse_next_event(auparse_state_t *au); + +/* Accessors to event data */ +const au_event_t *auparse_get_timestamp(auparse_state_t *au); +time_t auparse_get_time(auparse_state_t *au); +unsigned int auparse_get_milli(auparse_state_t *au); +unsigned long auparse_get_serial(auparse_state_t *au); +const char *auparse_get_node(auparse_state_t *au); +int auparse_node_compare(au_event_t *e1, au_event_t *e2); +int auparse_timestamp_compare(au_event_t *e1, au_event_t *e2); +unsigned int auparse_get_num_records(auparse_state_t *au); + +/* Functions that traverse records in the same event */ +int auparse_first_record(auparse_state_t *au); +int auparse_next_record(auparse_state_t *au); +int auparse_goto_record_num(auparse_state_t *au, unsigned int num); + +/* Accessors to record data */ +int auparse_get_type(auparse_state_t *au); +const char *auparse_get_type_name(auparse_state_t *au); +unsigned int auparse_get_line_number(auparse_state_t *au); +const char *auparse_get_filename(auparse_state_t *au); +int auparse_first_field(auparse_state_t *au); +int auparse_next_field(auparse_state_t *au); +unsigned int auparse_get_num_fields(auparse_state_t *au); +const char *auparse_get_record_text(auparse_state_t *au); +const char *auparse_find_field(auparse_state_t *au, const char *name); +const char *auparse_find_field_next(auparse_state_t *au); + +/* Accessors to field data */ +const char *auparse_get_field_name(auparse_state_t *au); +const char *auparse_get_field_str(auparse_state_t *au); +int auparse_get_field_type(auparse_state_t *au); +int auparse_get_field_int(auparse_state_t *au); +const char *auparse_interpret_field(auparse_state_t *au); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/auparse/auparse.pc.in b/framework/src/audit/auparse/auparse.pc.in new file mode 100644 index 00000000..581287e8 --- /dev/null +++ b/framework/src/audit/auparse/auparse.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libauparse +Description: Library for apps that want to parse and interpret audit logs +Version: @VERSION@ +Libs: -L${libdir} -lauparse +Libs.private: -laudit +Cflags: -I${includedir} diff --git a/framework/src/audit/auparse/captab.h b/framework/src/audit/auparse/captab.h new file mode 100644 index 00000000..409fdb4e --- /dev/null +++ b/framework/src/audit/auparse/captab.h @@ -0,0 +1,62 @@ +/* captab.h -- + * Copyright 2007,2008,2012-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/capability.h + */ + + +_S(0, "chown" ) +_S(1, "dac_override" ) +_S(2, "dac_read_search" ) +_S(3, "fowner" ) +_S(4, "fsetid" ) +_S(5, "kill" ) +_S(6, "setgid" ) +_S(7, "setuid" ) +_S(8, "setpcap" ) +_S(9, "linux_immutable" ) +_S(10, "net_bind_service" ) +_S(11, "net_broadcast" ) +_S(12, "net_admin" ) +_S(13, "net_raw" ) +_S(14, "ipc_lock" ) +_S(15, "ipc_owner" ) +_S(16, "sys_module" ) +_S(17, "sys_rawio" ) +_S(18, "sys_chroot" ) +_S(19, "sys_ptrace" ) +_S(20, "sys_pacct" ) +_S(21, "sys_admin" ) +_S(22, "sys_boot" ) +_S(23, "sys_nice" ) +_S(24, "sys_resource" ) +_S(25, "sys_time" ) +_S(26, "sys_tty_config" ) +_S(27, "mknod" ) +_S(28, "lease" ) +_S(29, "audit_write" ) +_S(30, "audit_control" ) +_S(31, "setfcap" ) +_S(32, "mac_override" ) +_S(33, "mac_admin" ) +_S(34, "syslog" ) +_S(35, "wake_alarm" ) +_S(36, "block_suspend" ) +_S(37, "audit_read" ) diff --git a/framework/src/audit/auparse/clocktab.h b/framework/src/audit/auparse/clocktab.h new file mode 100644 index 00000000..bcb396fe --- /dev/null +++ b/framework/src/audit/auparse/clocktab.h @@ -0,0 +1,36 @@ +/* clocktab.h -- + * Copyright 2012,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/time.h + */ + +_S(0, "CLOCK_REALTIME" ) +_S(1, "CLOCK_MONOTONIC" ) +_S(2, "CLOCK_PROCESS_CPUTIME_ID" ) +_S(3, "CLOCK_THREAD_CPUTIME_ID" ) +_S(4, "CLOCK_MONOTONIC_RAW" ) +_S(5, "CLOCK_REALTIME_COARSE" ) +_S(6, "CLOCK_MONOTONIC_COARSE" ) +_S(7, "CLOCK_BOOTTIME" ) +_S(8, "CLOCK_REALTIME_ALARM" ) +_S(9, "CLOCK_BOOTTIME_ALARM" ) +_S(10, "CLOCK_SGI_CYCLE" ) +_S(11, "CLOCK_TAI" ) + diff --git a/framework/src/audit/auparse/clone-flagtab.h b/framework/src/audit/auparse/clone-flagtab.h new file mode 100644 index 00000000..503e84bc --- /dev/null +++ b/framework/src/audit/auparse/clone-flagtab.h @@ -0,0 +1,47 @@ +/* clone-flagtab.h -- + * Copyright 2007,2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/sched.h + */ + +_S(0x00000100, "CLONE_VM" ) +_S(0x00000200, "CLONE_FS" ) +_S(0x00000400, "CLONE_FILES" ) +_S(0x00000800, "CLONE_SIGHAND" ) +_S(0x00002000, "CLONE_PTRACE" ) +_S(0x00004000, "CLONE_VFORK" ) +_S(0x00008000, "CLONE_PARENT" ) +_S(0x00010000, "CLONE_THREAD" ) +_S(0x00020000, "CLONE_NEWNS" ) +_S(0x00040000, "CLONE_SYSVSEM" ) +_S(0x00080000, "CLONE_SETTLS" ) +_S(0x00100000, "CLONE_PARENT_SETTID" ) +_S(0x00200000, "CLONE_CHILD_CLEARTID" ) +_S(0x00400000, "CLONE_DETACHED" ) +_S(0x00800000, "CLONE_UNTRACED" ) +_S(0x01000000, "CLONE_CHILD_SETTID" ) +_S(0x02000000, "CLONE_STOPPED" ) +_S(0x04000000, "CLONE_NEWUTS" ) +_S(0x08000000, "CLONE_NEWIPC" ) +_S(0x10000000, "CLONE_NEWUSER" ) +_S(0x20000000, "CLONE_NEWPID" ) +_S(0x40000000, "CLONE_NEWNET" ) +_S(0x80000000, "CLONE_IO" ) + diff --git a/framework/src/audit/auparse/data_buf.c b/framework/src/audit/auparse/data_buf.c new file mode 100644 index 00000000..43b5999e --- /dev/null +++ b/framework/src/audit/auparse/data_buf.c @@ -0,0 +1,394 @@ +/* data_buf.c -- + * Copyright 2007,2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * John Dennis + */ + +/* + * gcc -DTEST -g data_buf.c -o data_buf + * gcc -DTEST -g data_buf.c -o data_buf && valgrind --leak-check=yes ./data_buf + */ + +/*****************************************************************************/ +/******************************** Documentation ******************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/******************************* Include Files *******************************/ +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "data_buf.h" + +/*****************************************************************************/ +/****************************** Internal Defines *****************************/ +/*****************************************************************************/ + +#ifndef MIN +#define MIN(a,b) (((a)<=(b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>=(b))?(a):(b)) +#endif + +//#define DEBUG 1 + +#ifdef DEBUG +#define DATABUF_VALIDATE(db) \ +{ \ + if (db->alloc_ptr == NULL || db->alloc_size == 0) { \ + assert(db->alloc_ptr == NULL); \ + assert(db->alloc_size == 0); \ + assert(db->len == 0); \ + } else { \ + assert(db->offset <= db->alloc_size); \ + assert(db->len <= db->alloc_size); \ + assert(db->offset+db->len <= db->alloc_size); \ + } \ +} +#else +#define DATABUF_VALIDATE(db) +#endif + +/*****************************************************************************/ +/************************** Internal Type Definitions ************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/********************** External Function Declarations *********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/********************** Internal Function Declarations *********************/ +/*****************************************************************************/ + +static int databuf_shift_data_to_beginning(DataBuf *db); +static int databuf_strcat(DataBuf *db, const char *str); + +/*****************************************************************************/ +/************************* External Global Variables ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************************* Internal Global Variables ***********************/ +/*****************************************************************************/ + +#ifdef DEBUG +static int debug = 0; +#endif + +/*****************************************************************************/ +/**************************** Inline Functions *****************************/ +/*****************************************************************************/ +static inline char *databuf_end(DataBuf *db) +{return (db->alloc_ptr == NULL) ? NULL : db->alloc_ptr+db->offset+db->len;} + +static inline char *databuf_alloc_end(DataBuf *db) +{return (db->alloc_ptr == NULL) ? NULL : db->alloc_ptr+db->alloc_size;} + +static inline int databuf_tail_size(DataBuf *db) +{return db->alloc_size - (db->offset+db->len);} + +static inline int databuf_tail_available(DataBuf *db, size_t append_len) +{return append_len <= databuf_tail_size(db);} + +static inline size_t databuf_free_size(DataBuf *db) +{return db->alloc_size-db->len;} + +/*****************************************************************************/ +/*************************** Internal Functions ****************************/ +/*****************************************************************************/ + +static int databuf_shift_data_to_beginning(DataBuf *db) +{ + DATABUF_VALIDATE(db); + if (db->flags & DATABUF_FLAG_PRESERVE_HEAD) return -1; + if (databuf_beg(db) == NULL) return 1; + if (db->offset) { + memmove(db->alloc_ptr, databuf_beg(db), db->len); + db->offset = 0; + } + DATABUF_VALIDATE(db); + return 1; +} + +/*****************************************************************************/ +/**************************** Exported Functions ***************************/ +/*****************************************************************************/ + +void databuf_print(DataBuf *db, int print_data, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (fmt) { + vprintf(fmt, ap); + } + printf("%salloc_size=%zu alloc_ptr=%p offset=%zu beg=%p len=%zu max_len=%zu flags=[", + fmt?" ":"", db->alloc_size, db->alloc_ptr, db->offset, databuf_beg(db), db->len, db->max_len); + + if (db->flags & DATABUF_FLAG_PRESERVE_HEAD) printf("PRESERVE_HEAD "); + if (db->flags & DATABUF_FLAG_STRING) printf("STRING "); + printf("]"); + + if (print_data) { + printf(" ["); + fwrite(databuf_beg(db), 1, db->len, stdout); + printf("]"); + } + printf("\n"); + va_end(ap); +} + +int databuf_init(DataBuf *db, size_t size, unsigned flags) +{ + db->alloc_ptr = NULL; + db->alloc_size = 0; + db->offset = 0; + db->len = 0; + db->max_len = 0; + db->flags = flags; + + if (size) { + if ((db->alloc_ptr = malloc(size))) { + db->alloc_size = size; + return 1; + } else { + return -1; + } + } + + // For strings intialize with initial NULL terminator + if (flags & DATABUF_FLAG_STRING) databuf_strcat(db, ""); + + return 1; +} + +void databuf_free(DataBuf *db) +{ + DATABUF_VALIDATE(db); + + if (db->alloc_ptr != NULL) { + free(db->alloc_ptr); + } + + db->alloc_ptr = NULL; + db->alloc_size = 0; + db->offset = 0; + db->len = 0; + db->max_len = 0; + + DATABUF_VALIDATE(db); +} + +int databuf_append(DataBuf *db, const char *src, size_t src_size) +{ + size_t new_size; + + DATABUF_VALIDATE(db); + + if (src == NULL || src_size == 0) return 0; + + new_size = db->len+src_size; + +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_append() size=%zd", src_size); +#endif + if ((new_size > db->alloc_size) || + ((db->flags & DATABUF_FLAG_PRESERVE_HEAD) && !databuf_tail_available(db, src_size))) { + /* not enough room, we must realloc */ + void *new_alloc; + + databuf_shift_data_to_beginning(db); + if ((new_alloc = realloc(db->alloc_ptr, new_size))) { + db->alloc_ptr = new_alloc; + db->alloc_size = new_size; + } else { + return -1; /* realloc failed */ + } + } else { + /* we can fit within current allocation, but can we append? */ + if (!databuf_tail_available(db, src_size)) { + /* we can't append in place, must create room at tail by shifting + data forward to the beginning of the allocation block */ + databuf_shift_data_to_beginning(db); + } + } +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_append() about to memmove()"); +#endif + /* pointers all set up and room availble, move the data and update */ + memmove(databuf_end(db), src, src_size); + db->len = new_size; + db->max_len = MAX(db->max_len, new_size); +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_append() conclusion"); +#endif + DATABUF_VALIDATE(db); + return 1; +} + +static int databuf_strcat(DataBuf *db, const char *str) +{ + size_t str_len; + + DATABUF_VALIDATE(db); + + if (str == NULL) return 0; + + // +1 so the data append also copies the NULL terminator + str_len = strlen(str) + 1; + + // If there is a NULL terminator exclude it so the subsequent + // data append produces a proper string concatenation + if (db->len > 0) { + char *last_char = databuf_end(db) - 1; + if (*last_char == 0) { + db->len--; // backup over NULL terminator + } + } + + // Copy string and NULL terminator + databuf_append(db, str, str_len); + + DATABUF_VALIDATE(db); + return 1; +} + +int databuf_advance(DataBuf *db, size_t advance) +{ + size_t actual_advance; + DATABUF_VALIDATE(db); + +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_advance() enter, advance=%zd", advance); +#endif + actual_advance = MIN(advance, db->len); + db->offset += actual_advance; + db->len -= actual_advance; + +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_advance() leave, actual_advance=%zd", actual_advance); +#endif + DATABUF_VALIDATE(db); + if (advance == actual_advance) { + return 1; + } else { + errno = ESPIPE; // Illegal seek + return -1; + } +} + +int databuf_reset(DataBuf *db) +{ +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_reset() entry"); +#endif + if (!(db->flags & DATABUF_FLAG_PRESERVE_HEAD)) return -1; + db->offset = 0; + db->len = MIN(db->alloc_size, db->max_len); +#ifdef DEBUG + if (debug) databuf_print(db, 1, "databuf_reset() exit"); +#endif + return 1; +} + +/*****************************************************************************/ +/******************************* Test Program ******************************/ +/*****************************************************************************/ + +#ifdef TEST +static char *make_data(size_t size, const char *fill) { + int n=0; + char *data = malloc(size); + + if (data == NULL) { + fprintf(stderr, "ERROR: make_data malloc failed\n"); + exit(1); + } + + n += snprintf(data, size, "%d", size); + while (n < size) { + n += snprintf(data+n, size-n, "%s", fill); + } + return data; +} + +int main(int argc, char **argv) +{ + size_t size = 0; + DataBuf buf; + char *data; + + assert(databuf_init(&buf, size, DATABUF_FLAG_STRING)); + databuf_print(&buf, 1, "after init size=%d", size); + +#if 1 + data = "a"; + assert(databuf_strcat(&buf, data)); + databuf_print(&buf, 1, "after strcat(%s)", data); + + data = "bb"; + assert(databuf_strcat(&buf, data)); + databuf_print(&buf, 1, "after strcat(%s)", data); + + data = "ccc"; + assert(databuf_strcat(&buf, data)); + databuf_print(&buf, 1, "after strcat(%s)", data); + +#endif + + databuf_free(&buf); + +#if 0 + assert(databuf_init(&buf, size, 0)); + databuf_print(&buf, 1, "after init size=%d", size); + + size = 8; + data = make_data(size, "a"); + assert(databuf_append(&buf, data, size)); + databuf_print(&buf, 1, "after append size=%d", size); + assert(databuf_append(&buf, data, size)); + free(data); + databuf_print(&buf, 1, "after append size=%d", size); + + assert(databuf_advance(&buf, 4)); + databuf_print(&buf, 1, "after databuf_advance(%d", 4); + + size = 5; + data = make_data(size, "b"); + assert(databuf_append(&buf, data, size)); + free(data); + databuf_print(&buf, 1, "after append size=%d", size); + size = 7; + data = make_data(size, "c"); + assert(databuf_append(&buf, data, size)); + free(data); + databuf_print(&buf, 1, "after append size=%d", size); + + databuf_free(&buf); +#endif + exit(0); +} +#endif diff --git a/framework/src/audit/auparse/data_buf.h b/framework/src/audit/auparse/data_buf.h new file mode 100644 index 00000000..66323fb7 --- /dev/null +++ b/framework/src/audit/auparse/data_buf.h @@ -0,0 +1,80 @@ +/* data_buf.h -- + * Copyright 2007 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * John Dennis + */ + +#ifndef DATA_BUF_HEADER +#define DATA_BUF_HEADER + +/*****************************************************************************/ +/******************************* Include Files *******************************/ +/*****************************************************************************/ +#include "config.h" +#include "private.h" + +/*****************************************************************************/ +/*********************************** Defines *********************************/ +/*****************************************************************************/ + +#define DATABUF_FLAG_PRESERVE_HEAD (1 << 0) +#define DATABUF_FLAG_STRING (2 << 0) + + +/*****************************************************************************/ +/******************************* Type Definitions ****************************/ +/*****************************************************************************/ + +typedef struct Databuf { + unsigned flags; + size_t alloc_size; + char *alloc_ptr; + size_t offset; + size_t len; + size_t max_len; +} DataBuf; + +/*****************************************************************************/ +/************************* External Global Variables ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/***************************** Inline Functions ****************************/ +/*****************************************************************************/ + +static inline char *databuf_beg(DataBuf *db) +{return (db->alloc_ptr == NULL) ? NULL : db->alloc_ptr+db->offset;} + +/*****************************************************************************/ +/**************************** Exported Functions ***************************/ +/*****************************************************************************/ + +void databuf_print(DataBuf *db, int print_data, char *fmt, ...) hidden +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))); +#else + ; +#endif +int databuf_init(DataBuf *db, size_t size, unsigned flags) hidden; +void databuf_free(DataBuf *db) hidden; +int databuf_append(DataBuf *db, const char *src, size_t src_size) hidden; +int databuf_advance(DataBuf *db, size_t advance) hidden; +int databuf_reset(DataBuf *db) hidden; + +#endif diff --git a/framework/src/audit/auparse/ellist.c b/framework/src/audit/auparse/ellist.c new file mode 100644 index 00000000..e5b60264 --- /dev/null +++ b/framework/src/audit/auparse/ellist.c @@ -0,0 +1,428 @@ +/* +* ellist.c - Minimal linked list library +* Copyright (c) 2006-08,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#include +#include +#include +#include +#include +#include "ellist.h" +#include "interpret.h" + +static const char key_sep[2] = { AUDIT_KEY_SEPARATOR, 0 }; + +void aup_list_create(event_list_t *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; + l->e.milli = 0L; + l->e.sec = 0L; + l->e.serial = 0L; + l->e.host = NULL; +} + +static void aup_list_last(event_list_t *l) +{ + register rnode* window; + + if (l->head == NULL) + return; + + window = l->head; + while (window->next) + window = window->next; + l->cur = window; +} + +rnode *aup_list_next(event_list_t *l) +{ + if (l->cur) + l->cur = l->cur->next; + return l->cur; +} + +/* + * * This function does encoding of "untrusted" names just like the kernel + * */ +static char *_audit_c2x(char *final, const char *buf, unsigned int size) +{ + unsigned int i; + char *ptr = final; + const char *hex = "0123456789ABCDEF"; + + for (i=0; i>4]; /* Upper nibble */ + *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ + } + *ptr = 0; + return final; +} + +static char *escape(const char *tmp) +{ + char *name; + const unsigned char *p = (unsigned char *)tmp; + while (*p) { + if (*p == '"' || *p < 0x21 || *p > 0x7e) { + int len = strlen(tmp); + name = malloc((2*len)+1); + return _audit_c2x(name, tmp, len); + } + p++; + } + if (asprintf(&name, "\"%s\"", tmp) < 0) + name = NULL; + return name; +} + +/* This funtion does the heavy duty work of splitting a record into + * its little tiny pieces */ +static int parse_up_record(rnode* r) +{ + char *ptr, *buf, *saved=NULL; + int offset = 0; + + buf = strdup(r->record); + ptr = audit_strsplit_r(buf, &saved); + if (ptr == NULL) { + free(buf); + return -1; + } + + do { // If there's an '=' sign, its a keeper + nvnode n; + char *val = strchr(ptr, '='); + if (val) { + int len; + + // If name is 'msg=audit' throw it away + if (*ptr == 'm' && strncmp(ptr, "msg=", 4) == 0) { + if (ptr[4] == 'a') + continue; + + // If name is 'msg='' chop off and see + // if there is still a = in the string. + else if (ptr[4] == '\'') { + ptr += 5; + val = strchr(ptr, '='); + if (val == NULL) + continue; + } + } + + // Split the string + *val = 0; + val++; + + // Remove beginning cruft of name + if (*ptr == '(') + ptr++; + n.name = strdup(ptr); + n.val = strdup(val); + // Remove trailing punctuation + len = strlen(n.val); + if (len && n.val[len-1] == ':') { + n.val[len-1] = 0; + len--; + } + if (len && n.val[len-1] == ',') { + n.val[len-1] = 0; + len--; + } + if (len && n.val[len-1] == '\'') { + n.val[len-1] = 0; + len--; + } + if (len && n.val[len-1] == ')') { + if (strcmp(n.val, "(none)") && + strcmp(n.val, "(null)")) { + n.val[len-1] = 0; + len--; + } + } + // Make virtual keys or just store it + if (strcmp(n.name, "key") == 0 && *n.val != '(') { + if (*n.val == '"') + nvlist_append(&r->nv, &n); + else { + char *key, *ptr, *saved2; + + key = (char *)au_unescape(n.val); + if (key == NULL) { + // Malformed key - save as is + nvlist_append(&r->nv, &n); + continue; + } + ptr = strtok_r(key, key_sep, &saved2); + free(n.name); + free(n.val); + while (ptr) { + n.name = strdup("key"); + n.val = escape(ptr); + nvlist_append(&r->nv, &n); + ptr = strtok_r(NULL, + key_sep, &saved2); + } + free(key); + } + continue; + } else + nvlist_append(&r->nv, &n); + + // Do some info gathering for use later + if (r->nv.cnt == 1 && strcmp(n.name, "node") == 0) + offset = 1; // if node, some positions changes + else if (r->nv.cnt == (1 + offset) && + strcmp(n.name, "type") == 0) { + r->type = audit_name_to_msg_type(n.val); + } else if (r->nv.cnt == (2 + offset) && + strcmp(n.name, "arch")== 0){ + unsigned int ival; + errno = 0; + ival = strtoul(n.val, NULL, 16); + if (errno) + r->machine = -2; + else + r->machine = audit_elf_to_machine(ival); + } else if (r->nv.cnt == (3 + offset) && + strcmp(n.name, "syscall") == 0){ + errno = 0; + r->syscall = strtoul(n.val, NULL, 10); + if (errno) + r->syscall = -1; + } else if (r->nv.cnt == (6 + offset) && + strcmp(n.name, "a0") == 0){ + errno = 0; + r->a0 = strtoull(n.val, NULL, 16); + if (errno) + r->a0 = -1LL; + } else if (r->nv.cnt == (7 + offset) && + strcmp(n.name, "a1") == 0){ + errno = 0; + r->a1 = strtoull(n.val, NULL, 16); + if (errno) + r->a1 = -1LL; + } + } else if (r->type == AUDIT_AVC || r->type == AUDIT_USER_AVC) { + // We special case these 2 fields because selinux + // avc messages do not label these fields. + n.name = NULL; + if (nvlist_get_cnt(&r->nv) == (1 + offset)) { + // skip over 'avc:' + if (strncmp(ptr, "avc", 3) == 0) + continue; + n.name = strdup("seresult"); + } else if (nvlist_get_cnt(&r->nv) == (2 + offset)) { + // skip over open brace + if (*ptr == '{') { + int total = 0, len; + char tmpctx[256], *to; + tmpctx[0] = 0; + to = tmpctx; + ptr = audit_strsplit_r(NULL, &saved); + while (ptr && *ptr != '}') { + len = strlen(ptr); + if ((len+1) >= (256-total)) { + free(buf); + return -1; + } + if (tmpctx[0]) { + to = stpcpy(to, ","); + total++; + } + to = stpcpy(to, ptr); + total += len; + ptr = audit_strsplit_r(NULL, + &saved); + } + n.name = strdup("seperms"); + n.val = strdup(tmpctx); + nvlist_append(&r->nv, &n); + continue; + } + } else + continue; + n.val = strdup(ptr); + nvlist_append(&r->nv, &n); + } + // FIXME: There should be an else here to catch ancillary data + } while((ptr = audit_strsplit_r(NULL, &saved))); + + free(buf); + r->nv.cur = r->nv.head; // reset to beginning + return 0; +} + +int aup_list_append(event_list_t *l, char *record, int list_idx, + unsigned int line_number) +{ + rnode* r; + + if (record == NULL) + return -1; + + // First step is build rnode + r = malloc(sizeof(rnode)); + if (r == NULL) + return -1; + + r->record = record; + r->type = 0; + r->a0 = 0LL; + r->a1 = 0LL; + r->machine = -1; + r->syscall = -1; + r->item = l->cnt; + r->list_idx = list_idx; + r->line_number = line_number; + r->next = NULL; + nvlist_create(&r->nv); + + // if we are at top, fix this up + if (l->head == NULL) + l->head = r; + else { // Otherwise add pointer to newnode + aup_list_last(l); + l->cur->next = r; + } + + // make newnode current + l->cur = r; + l->cnt++; + + // Then parse the record up into nvlist + return parse_up_record(r); +} + +void aup_list_clear(event_list_t* l) +{ + rnode* nextnode; + register rnode* current; + + if (l == NULL) + return; + + current = l->head; + while (current) { + nextnode=current->next; + nvlist_clear(¤t->nv); + free(current->record); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; + l->e.milli = 0L; + l->e.sec = 0L; + l->e.serial = 0L; + free((char *)l->e.host); + l->e.host = NULL; +} + +/*int aup_list_get_event(event_list_t* l, au_event_t *e) +{ + if (l == NULL || e == NULL) + return 0; + + e->sec = l->e.sec; + e->milli = l->e.milli; + e->serial = l->e.serial; + if (l->e.host) + e->host = strdup(l->e.host); + else + e->host = NULL; + return 1; +} */ + +int aup_list_set_event(event_list_t* l, au_event_t *e) +{ + if (l == NULL || e == NULL) + return 0; + + l->e.sec = e->sec; + l->e.milli = e->milli; + l->e.serial = e->serial; + l->e.host = e->host; // Take custody of the memory + e->host = NULL; + return 1; +} + +rnode *aup_list_find_rec(event_list_t *l, int i) +{ + register rnode* window; + + window = l->head; /* start at the beginning */ + while (window) { + if (window->type == i) { + l->cur = window; + return window; + } else + window = window->next; + } + return NULL; +} + +rnode *aup_list_goto_rec(event_list_t *l, int i) +{ + register rnode* window; + + window = l->head; /* start at the beginning */ + while (window) { + if (window->item == i) { + l->cur = window; + return window; + } else + window = window->next; + } + return NULL; +} + +rnode *aup_list_find_rec_range(event_list_t *l, int low, int high) +{ + register rnode* window; + + if (high <= low) + return NULL; + + window = l->head; /* Start at the beginning */ + while (window) { + if (window->type >= low && window->type <= high) { + l->cur = window; + return window; + } else + window = window->next; + } + return NULL; +} + +int aup_list_first_field(event_list_t *l) +{ + if (l->cur) { + nvlist_first(&l->cur->nv); + return 1; + } else + return 0; +} + diff --git a/framework/src/audit/auparse/ellist.h b/framework/src/audit/auparse/ellist.h new file mode 100644 index 00000000..2b43a68d --- /dev/null +++ b/framework/src/audit/auparse/ellist.h @@ -0,0 +1,66 @@ +/* +* ellist.h - Header file for ellist.c +* Copyright (c) 2006-07 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#ifndef ELLIST_HEADER +#define ELLIST_HEADER + +#include "config.h" +#include "private.h" +#include "auparse-defs.h" +#include +#include "nvlist.h" + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + rnode *head; // List head + rnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list + + // Data we add as 1 per event + au_event_t e; // event - time & serial number +} event_list_t; + +void aup_list_create(event_list_t *l) hidden; +void aup_list_clear(event_list_t* l) hidden; +static inline unsigned int aup_list_get_cnt(event_list_t *l) { return l->cnt; } +static inline void aup_list_first(event_list_t *l) { l->cur = l->head; } +static inline rnode *aup_list_get_cur(event_list_t *l) { return l->cur; } +rnode *aup_list_next(event_list_t *l) hidden; +int aup_list_append(event_list_t *l, char *record, int list_idx, unsigned int line_number) hidden; +//int aup_list_get_event(event_list_t* l, au_event_t *e) hidden; +int aup_list_set_event(event_list_t* l, au_event_t *e) hidden; + +/* Given a message type, find the matching node */ +rnode *aup_list_find_rec(event_list_t *l, int i) hidden; + +/* Seek to a specific record number */ +rnode *aup_list_goto_rec(event_list_t *l, int i) hidden; + +/* Given two message types, find the first matching node */ +rnode *aup_list_find_rec_range(event_list_t *l, int low, int high) hidden; + +int aup_list_first_field(event_list_t *l) hidden; + +#endif + diff --git a/framework/src/audit/auparse/epoll_ctl.h b/framework/src/audit/auparse/epoll_ctl.h new file mode 100644 index 00000000..3d58a2bf --- /dev/null +++ b/framework/src/audit/auparse/epoll_ctl.h @@ -0,0 +1,27 @@ +/* epoll_ctl.h -- + * Copyright 2008,2012,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/eventpoll.h + */ + +_S(1, "EPOLL_CTL_ADD" ) +_S(2, "EPOLL_CTL_DEL" ) +_S(3, "EPOLL_CTL_MOD" ) + diff --git a/framework/src/audit/auparse/expression.c b/framework/src/audit/auparse/expression.c new file mode 100644 index 00000000..6bed45ba --- /dev/null +++ b/framework/src/audit/auparse/expression.c @@ -0,0 +1,1111 @@ +/* +* expression.c - Expression parsing and handling +* Copyright (C) 2008,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Miloslav Trmač +* Steve Grubb extended timestamp +*/ + +#include +#include +#include +#include +#include + +#include "expression.h" + + /* Utilities */ + +/* Free EXPR and all its subexpressions. */ +void +expr_free(struct expr *expr) +{ + switch (expr->op) { + case EO_NOT: + expr_free(expr->v.sub[0]); + break; + + case EO_AND: case EO_OR: + expr_free(expr->v.sub[0]); + expr_free(expr->v.sub[1]); + break; + + case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ: + case EO_INTERPRETED_NE: case EO_VALUE_EQ: case EO_VALUE_NE: + case EO_VALUE_LT: case EO_VALUE_LE: case EO_VALUE_GT: case EO_VALUE_GE: + if (expr->virtual_field == 0) + free(expr->v.p.field.name); + if (expr->precomputed_value == 0) + free(expr->v.p.value.string); + break; + + case EO_FIELD_EXISTS: + assert(expr->virtual_field == 0); + free(expr->v.p.field.name); + break; + + case EO_REGEXP_MATCHES: + regfree(expr->v.regexp); + free(expr->v.regexp); + break; + + default: + abort(); + } + free(expr); +} + + /* Expression parsing. */ + +/* The formal grammar: + + start: or-expression + + or-expression: and-expression + or-expression: or-expression || and-expression + + and-expression: primary-expression + and-expression: and-expression && primary-expression + + primary-expression: ! primary-expression + primary-expression: ( or-expression ) + primary-expression: comparison-expression + + comparison-expression: field op value + comparison-expression: field-escape "regexp" regexp-value + field: string + field: field-escape string + value: string + regexp-value: string + regexp-value: regexp */ + +/* Token types */ +enum token_type { + /* EO_* */ + T_LEFT_PAREN = NUM_EO_VALUES, T_RIGHT_PAREN, T_STRING, T_REGEXP, + T_FIELD_ESCAPE, T_UNKNOWN, T_EOF +}; + +/* Expression parsing status */ +struct parsing { + char **error; /* Error message destination. */ + enum token_type token; + const char *token_start; /* Original "src" value */ + int token_len; /* int because it must be usable in %.*s */ + char *token_value; /* Non-NULL only for T_STRING, until used */ + const char *src; /* Expression source, after the current token */ +}; + +static struct expr *parse_or(struct parsing *p); + +/* Allocate SIZE bytes. + On error, return NULL and try to set *P->ERROR. */ +static void * +parser_malloc(struct parsing *p, size_t size) +{ + void *res; + + res = malloc(size); + if (res != NULL || size == 0) + return res; + *p->error = strdup("Out of memory"); + return NULL; +} + +/* Reallocate PTR to SIZE bytes. + On error, free(PTR), return NULL and try to set *P->ERROR. + NOTE: realloc() does not free(PTR), this function does. */ +static void * +parser_realloc(struct parsing *p, void *ptr, size_t size) +{ + void *res; + + res = realloc(ptr, size); + if (res != NULL || size == 0) + return res; + free(ptr); + *p->error = strdup("Out of memory"); + return NULL; +} + +/* Discard P->token_value, if any, and parse the next token in P->src. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + -1. */ +static int +lex(struct parsing *p) +{ + free(p->token_value); + p->token_value = NULL; + while (*p->src == ' ' || *p->src == '\t' || *p->src == '\n') + p->src++; + p->token_start = p->src; + switch (*p->src) { + case '\0': + p->token = T_EOF; + break; + + case '!': + p->src++; + if (*p->src == '=' && p->src[1] == '=') { + p->src += 2; + p->token = EO_VALUE_NE; + break; + } + p->token = EO_NOT; + break; + + case '"': case '/': { + char *buf, delimiter; + size_t dest, buf_size; + + delimiter = *p->src; + buf_size = 8; + buf = parser_malloc(p, buf_size); + if (buf == NULL) + return -1; + p->src++; + dest = 0; + while (*p->src != delimiter) { + if (*p->src == '\0') { + *p->error = strdup("Terminating delimiter " + "missing"); + free(buf); + return -1; + } + if (*p->src == '\\') { + p->src++; + if (*p->src != '\\' && *p->src != delimiter) { + if (asprintf(p->error, "Unknown escape " + "sequence ``\\%c''", + *p->src) < 0) + *p->error = NULL; + free(buf); + return -1; + } + } + /* +1: make sure there is space for the terminating + NUL. */ + if (dest + 1 >= buf_size) { + if (buf_size > SIZE_MAX / 2) { + *p->error = strdup("Delimited string " + "too long"); + free(buf); + return -1; + } + buf_size *= 2; + buf = parser_realloc(p, buf, buf_size); + if (buf == NULL) { + *p->error = strdup("Out of memory"); + return -1; + } + } + buf[dest] = *p->src; + dest++; + p->src++; + } + p->src++; + buf[dest] = '\0'; + p->token_value = parser_realloc(p, buf, dest + 1); + if (p->token_value == NULL) + return -1; + p->token = delimiter == '/' ? T_REGEXP : T_STRING; + break; + } + + case '&': + p->src++; + if (*p->src == '&') { + p->src++; + p->token = EO_AND; + break; + } + p->token = T_UNKNOWN; + break; + + case '(': + p->src++; + p->token = T_LEFT_PAREN; + break; + + case ')': + p->src++; + p->token = T_RIGHT_PAREN; + break; + + case '<': + p->src++; + if (*p->src == '=') { + p->src++; + p->token = EO_VALUE_LE; + break; + } + p->token = EO_VALUE_LT; + break; + + case '=': + p->src++; + if (*p->src == '=') { + p->src++; + p->token = EO_VALUE_EQ; + break; + } + p->token = T_UNKNOWN; + break; + + case '>': + p->src++; + if (*p->src == '=') { + p->src++; + p->token = EO_VALUE_GE; + break; + } + p->token = EO_VALUE_GT; + break; + + case '\\': + p->src++; + p->token = T_FIELD_ESCAPE; + break; + + case '|': + p->src++; + if (*p->src == '|') { + p->src++; + p->token = EO_OR; + break; + } + p->token = T_UNKNOWN; + break; + + case 'i': + if (p->src[1] == '=') { + p->src += 2; + p->token = EO_INTERPRETED_EQ; + break; + } else if (p->src[1] == '!' && p->src[2] == '=') { + p->src += 3; + p->token = EO_INTERPRETED_NE; + break; + } + goto unquoted_string; + + case 'r': + if (p->src[1] == '=') { + p->src += 2; + p->token = EO_RAW_EQ; + break; + } else if (p->src[1] == '!' && p->src[2] == '=') { + p->src += 3; + p->token = EO_RAW_NE; + break; + } + goto unquoted_string; + + default: + /* This assumes ASCII */ + assert ('Z' == 'A' + 25 && 'z' == 'a' + 25); +#define IS_UNQUOTED_STRING_CHAR(C) \ + (((C) >= 'a' && (C) <= 'z') \ + || ((C) >= 'A' && (C) <= 'Z') \ + || ((C) >= '0' && (C) <= '9') \ + || (C) == '_') + if (IS_UNQUOTED_STRING_CHAR(*p->src)) { + size_t len; + + unquoted_string: + do + p->src++; + while (IS_UNQUOTED_STRING_CHAR(*p->src)); + len = p->src - p->token_start; + p->token_value = parser_malloc(p, len + 1); + if (p->token_value == NULL) + return -1; + memcpy(p->token_value, p->token_start, len); + p->token_value[len] = '\0'; + p->token = T_STRING; + break; + } + p->src++; + p->token = T_UNKNOWN; + break; + } + if (p->src - p->token_start > INT_MAX) { + *p->error = strdup("Token too long"); + return -1; + } + p->token_len = p->src - p->token_start; + return 0; +} + +/* Parse an escaped field NAME to DEST. + Return 0 on success, -1 if NAME is unknown. */ +static int +parse_escaped_field_name(enum field_id *dest, const char *name) +{ + if (strcmp(name, "timestamp") == 0) + *dest = EF_TIMESTAMP; + else if (strcmp(name, "record_type") == 0) + *dest = EF_RECORD_TYPE; + else if (strcmp(name, "timestamp_ex") == 0) + *dest = EF_TIMESTAMP_EX; + else + return -1; + return 0; +} + +/* Parse a \timestamp field value in P->token_value to DEST. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + -1. */ +static int +parse_timestamp_value(struct expr *dest, struct parsing *p) +{ + intmax_t sec; + + assert(p->token == T_STRING); + /* FIXME: other formats? */ + if (sscanf(p->token_value, "ts:%jd.%u:%u", &sec, + &dest->v.p.value.timestamp_ex.milli, + &dest->v.p.value.timestamp_ex.serial) != 3) { + if (sscanf(p->token_value, "ts:%jd.%u", &sec, + &dest->v.p.value.timestamp.milli) != 2) { + if (asprintf(p->error, "Invalid timestamp value `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + return -1; + } + } + /* FIXME: validate milli */ + dest->v.p.value.timestamp.sec = sec; + if (dest->v.p.value.timestamp.sec != sec) { + if (asprintf(p->error, "Timestamp overflow in `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + return -1; + } + dest->precomputed_value = 1; + return 0; +} + +/* Parse a \record_type field value in P->token_value to DEST. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + -1. */ +static int +parse_record_type_value(struct expr *dest, struct parsing *p) +{ + int type; + + assert(p->token == T_STRING); + type = audit_name_to_msg_type(p->token_value); + if (type < 0) { + if (asprintf(p->error, "Invalid record type `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + return -1; + } + dest->v.p.value.int_value = type; + dest->precomputed_value = 1; + return 0; +} + +/* Parse a virtual field value in P->token_value to DEST. + On success, return 0. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static int +parse_virtual_field_value(struct expr *dest, struct parsing *p) +{ + switch (dest->v.p.field.id) { + case EF_TIMESTAMP: + return parse_timestamp_value(dest, p); + + case EF_RECORD_TYPE: + return parse_record_type_value(dest, p); + + case EF_TIMESTAMP_EX: + return parse_timestamp_value(dest, p); + + default: + abort(); + } +} + +/* Parse a \regexp comparison-expression string in *P, with \regexp parsed. + Use or free EXPR. + On success, return the parsed comparison-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_comparison_regexp(struct parsing *p, struct expr *res) +{ + int err; + + if (lex(p) != 0) + goto err_res; + if (p->token != T_STRING && p->token != T_REGEXP) { + if (asprintf(p->error, "Regexp expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_res; + } + res->v.regexp = parser_malloc(p, sizeof(*res->v.regexp)); + if (res->v.regexp == NULL) + goto err_res; + err = regcomp(res->v.regexp, p->token_value, REG_EXTENDED | REG_NOSUB); + if (err != 0) { + size_t err_size; + char *err_msg; + + err_size = regerror(err, res->v.regexp, NULL, 0); + err_msg = parser_malloc(p, err_size); + if (err_msg == NULL) + goto err_res_regexp; + regerror(err, res->v.regexp, err_msg, err_size); + if (asprintf(p->error, "Invalid regexp: %s", err_msg) < 0) + *p->error = NULL; + free(err_msg); + goto err_res_regexp; + } + res->op = EO_REGEXP_MATCHES; + if (lex(p) != 0) { + expr_free(res); + return NULL; + } + return res; + +err_res_regexp: + free(res->v.regexp); +err_res: + free(res); + return NULL; +} + +/* Parse a comparison-expression string in *P. + On success, return the parsed comparison-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_comparison(struct parsing *p) +{ + struct expr *res; + + res = parser_malloc(p, sizeof(*res)); + if (res == NULL) + return NULL; + if (p->token == T_FIELD_ESCAPE) { + if (lex(p) != 0) + goto err_res; + if (p->token != T_STRING) { + *p->error = strdup("Field name expected after field " + "escape"); + goto err_res; + } + if (strcmp(p->token_value, "regexp") == 0) + return parse_comparison_regexp(p, res); + res->virtual_field = 1; + if (parse_escaped_field_name(&res->v.p.field.id, p->token_value) + != 0) { + if (asprintf(p->error, + "Unknown escaped field name `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_res; + } + } else { + assert(p->token == T_STRING); + res->virtual_field = 0; + res->v.p.field.name = p->token_value; + p->token_value = NULL; + } + if (lex(p) != 0) + goto err_field; + switch (p->token) { + case EO_RAW_EQ: case EO_RAW_NE: case EO_INTERPRETED_EQ: + case EO_INTERPRETED_NE: + res->op = p->token; + if (lex(p) != 0) + goto err_field; + if (p->token != T_STRING) { + if (asprintf(p->error, "Value expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_field; + } + res->precomputed_value = 0; + res->v.p.value.string = p->token_value; + p->token_value = NULL; + if (lex(p) != 0) { + expr_free(res); + return NULL; + } + break; + + case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE: + case EO_VALUE_GT: case EO_VALUE_GE: + res->op = p->token; + if (lex(p) != 0) + goto err_field; + if (p->token != T_STRING) { + if (asprintf(p->error, "Value expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_field; + } + if (res->virtual_field == 0) { + if (asprintf(p->error, "Field `%s' does not support " + "value comparison", + res->v.p.field.name) < 0) + *p->error = NULL; + goto err_field; + } else { + if (parse_virtual_field_value(res, p) != 0) + goto err_field; + } + if (lex(p) != 0) { + expr_free(res); + return NULL; + } + break; + + default: + if (asprintf(p->error, "Operator expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_field; + } + return res; + +err_field: + if (res->virtual_field == 0) + free(res->v.p.field.name); +err_res: + free(res); + return NULL; +} + +/* Parse a primary-expression string in *P. + On success, return the parsed primary-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_primary(struct parsing *p) +{ + struct expr *e; + + switch (p->token) { + case EO_NOT: { + struct expr *res; + + if (lex(p) != 0) + return NULL; + e = parse_primary(p); + if (e == NULL) + return NULL; + res = parser_malloc(p, sizeof(*res)); + if (res == NULL) + goto err_e; + res->op = EO_NOT; + res->v.sub[0] = e; + return res; + } + + case T_LEFT_PAREN: { + if (lex(p) != 0) + return NULL; + e = parse_or(p); + if (e == NULL) + return NULL; + if (p->token != T_RIGHT_PAREN) { + if (asprintf(p->error, + "Right paren expected, got `%.*s'", + p->token_len, p->token_start) < 0) + *p->error = NULL; + goto err_e; + } + if (lex(p) != 0) + goto err_e; + return e; + } + + case T_FIELD_ESCAPE: case T_STRING: + return parse_comparison(p); + + default: + if (asprintf(p->error, "Unexpected token `%.*s'", p->token_len, + p->token_start) < 0) + *p->error = NULL; + return NULL; + } +err_e: + expr_free(e); + return NULL; +} + +/* Parse an and-expression string in *P. + On success, return the parsed and-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_and(struct parsing *p) +{ + struct expr *res; + + res = parse_primary(p); + if (res == NULL) + return NULL; + while (p->token == EO_AND) { + struct expr *e2, *e; + + if (lex(p) != 0) + goto err_res; + e2 = parse_primary(p); + if (e2 == NULL) + goto err_res; + e = parser_malloc(p, sizeof(*e)); + if (e == NULL) { + expr_free(e2); + goto err_res; + } + e->op = EO_AND; + e->v.sub[0] = res; + e->v.sub[1] = e2; + res = e; + } + return res; + +err_res: + expr_free(res); + return NULL; +} + +/* Parse an or-expression string in *P. + On success, return the parsed or-expression. + On error, set *P->ERROR to an error string (for free()) or NULL, and return + NULL. */ +static struct expr * +parse_or(struct parsing *p) +{ + struct expr *res; + + res = parse_and(p); + if (res == NULL) + return NULL; + while (p->token == EO_OR) { + struct expr *e2, *e; + + if (lex(p) != 0) + goto err_res; + e2 = parse_and(p); + if (e2 == NULL) + goto err_res; + e = parser_malloc(p, sizeof(*e)); + if (e == NULL) { + expr_free(e2); + goto err_res; + } + e->op = EO_OR; + e->v.sub[0] = res; + e->v.sub[1] = e2; + res = e; + } + return res; + +err_res: + expr_free(res); + return NULL; +} + +/* Parse STRING. + On success, return the parsed expression tree. + On error, set *ERROR to an error string (for free()) or NULL, and return + NULL. (*ERROR == NULL is allowed to handle out-of-memory errors) */ +struct expr * +expr_parse(const char *string, char **error) +{ + struct parsing p; + struct expr *res; + + p.error = error; + p.token_value = NULL; + p.src = string; + if (lex(&p) != 0) + goto err; + if (p.token == T_EOF) { + *error = strdup("Empty expression"); + goto err; + } + res = parse_or(&p); + if (res != NULL && p.token != T_EOF) { + expr_free(res); + if (asprintf(error, "Unexpected trailing token `%.*s'", + p.token_len, p.token_start) < 0) + *error = NULL; + goto err; + } + free(p.token_value); + return res; + +err: + free(p.token_value); + return NULL; +} + + /* Manual expression creation */ + +/* Create a comparison-expression for FIELD, OP and VALUE. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_comparison(const char *field, unsigned op, const char *value) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + goto err; + assert(op == EO_RAW_EQ || op == EO_RAW_NE || op == EO_INTERPRETED_EQ + || op == EO_INTERPRETED_NE); + res->op = op; + res->virtual_field = 0; + res->precomputed_value = 0; + res->v.p.field.name = strdup(field); + if (res->v.p.field.name == NULL) + goto err_res; + res->v.p.value.string = strdup(value); + if (res->v.p.value.string == NULL) + goto err_field; + return res; + +err_field: + free(res->v.p.field.name); +err_res: + free(res); +err: + return NULL; +} + +/* Create an extended timestamp comparison-expression for with OP, SEC, + MILLI, and SERIAL. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_timestamp_comparison_ex(unsigned op, time_t sec, unsigned milli, + unsigned serial) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + return NULL; + assert(op == EO_VALUE_EQ || op == EO_VALUE_NE || op == EO_VALUE_LT + || op == EO_VALUE_LE || op == EO_VALUE_GT || op == EO_VALUE_GE); + res->op = op; + res->virtual_field = 1; + res->v.p.field.id = EF_TIMESTAMP_EX; + res->precomputed_value = 1; + res->v.p.value.timestamp_ex.sec = sec; + assert(milli < 1000); + res->v.p.value.timestamp_ex.milli = milli; + res->v.p.value.timestamp_ex.serial = serial; + return res; +} + +/* Create a timestamp comparison-expression for with OP, SEC, MILLI. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_timestamp_comparison(unsigned op, time_t sec, unsigned milli) +{ + return expr_create_timestamp_comparison_ex(op, sec, milli, 0); +} + +/* Create an EO_FIELD_EXISTS-expression for FIELD. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_field_exists(const char *field) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + goto err; + res->op = EO_FIELD_EXISTS; + res->virtual_field = 0; + res->v.p.field.name = strdup(field); + if (res->v.p.field.name == NULL) + goto err_res; + return res; + +err_res: + free(res); +err: + return NULL; +} + +/* Create a \regexp expression for regexp comparison. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr * +expr_create_regexp_expression(const char *regexp) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + goto err; + res->v.regexp = malloc(sizeof(*res->v.regexp)); + if (res->v.regexp == NULL) + goto err_res; + if (regcomp(res->v.regexp, regexp, REG_EXTENDED | REG_NOSUB) != 0) { + errno = EINVAL; + goto err_res_regexp; + } + res->op = EO_REGEXP_MATCHES; + return res; + +err_res_regexp: + free(res->v.regexp); +err_res: + free(res); +err: + return NULL; +} + +/* Create a binary expresion for OP and subexpressions E1 and E2. + On success, return the created expresion. + On error, set errno and return NULL. */ +struct expr * +expr_create_binary(unsigned op, struct expr *e1, struct expr *e2) +{ + struct expr *res; + + res = malloc(sizeof(*res)); + if (res == NULL) + return NULL; + assert(op == EO_AND || op ==EO_OR); + res->op = op; + res->v.sub[0] = e1; + res->v.sub[1] = e2; + return res; +} + + /* Expression evaluation */ + +/* Return the "raw" value of the field in EXPR for RECORD in AU->le. Set + *FREE_IT to 1 if the return value should free()'d. + Return NULL on error. */ +static char * +eval_raw_value(auparse_state_t *au, rnode *record, const struct expr *expr, + int *free_it) +{ + if (expr->virtual_field == 0) { + nvlist_first(&record->nv); + if (nvlist_find_name(&record->nv, expr->v.p.field.name) == 0) + return NULL; + *free_it = 0; + return (char *)nvlist_get_cur_val(&record->nv); + } + switch (expr->v.p.field.id) { + case EF_TIMESTAMP: case EF_RECORD_TYPE: case EF_TIMESTAMP_EX: + return NULL; + + default: + abort(); + } +} + +/* Return the "interpreted" value of the field in EXPR for RECORD in AU->le. + Set *FREE_IT to 1 if the return value should free()'d. + Return NULL on *error. */ +static char * +eval_interpreted_value(auparse_state_t *au, rnode *record, + const struct expr *expr, int *free_it) +{ + if (expr->virtual_field == 0) { + const char *res; + + nvlist_first(&record->nv); + if (nvlist_find_name(&record->nv, expr->v.p.field.name) == 0) + return NULL; + *free_it = 0; + res = nvlist_interp_cur_val(record); + if (res == NULL) + res = nvlist_get_cur_val(&record->nv); + return (char *)res; + } + switch (expr->v.p.field.id) { + case EF_TIMESTAMP: case EF_RECORD_TYPE: case EF_TIMESTAMP_EX: + return NULL; + + default: + abort(); + } +} + +/* Return -1, 0, 1 depending on comparing the field in EXPR with RECORD in AU. + Set *ERROR to 0 if OK, non-zero otherwise. */ +static int +compare_values(auparse_state_t *au, rnode *record, const struct expr *expr, + int *error) +{ + int res; + if (expr->virtual_field == 0) { + *error = 1; + return 0; + } + switch (expr->v.p.field.id) { + case EF_TIMESTAMP: + if (au->le.e.sec < expr->v.p.value.timestamp.sec) + res = -1; + else if (au->le.e.sec > expr->v.p.value.timestamp.sec) + res = 1; + else if (au->le.e.milli < expr->v.p.value.timestamp.milli) + res = -1; + else if (au->le.e.milli > expr->v.p.value.timestamp.milli) + res = 1; + else + res = 0; + break; + + case EF_RECORD_TYPE: + if (record->type < expr->v.p.value.int_value) + res = -1; + else if (record->type > expr->v.p.value.int_value) + res = 1; + else + res = 0; + break; + + case EF_TIMESTAMP_EX: + if (au->le.e.sec < expr->v.p.value.timestamp.sec) + res = -1; + else if (au->le.e.sec > expr->v.p.value.timestamp.sec) + res = 1; + else if (au->le.e.milli < expr->v.p.value.timestamp.milli) + res = -1; + else if (au->le.e.milli > expr->v.p.value.timestamp.milli) + res = 1; + else if (au->le.e.serial < expr->v.p.value.timestamp_ex.serial) + res = -1; + else if (au->le.e.serial > expr->v.p.value.timestamp_ex.serial) + res = 1; + else + res = 0; + break; + + default: + abort(); + } + *error = 0; + return res; +} + +/* Evaluate EXPR on RECORD in AU->le. + Return 1 if EXPR is true, 0 if it false or if it fails. + (No error reporting facility is provided; an invalid term is considered to + be false; e.g. !invalid is true.) */ +int +expr_eval(auparse_state_t *au, rnode *record, const struct expr *expr) +{ + switch (expr->op) { + case EO_NOT: + return !expr_eval(au, record, expr->v.sub[0]); + + case EO_AND: + return (expr_eval(au, record, expr->v.sub[0]) + && expr_eval(au, record, expr->v.sub[1])); + + case EO_OR: + return (expr_eval(au, record, expr->v.sub[0]) + || expr_eval(au, record, expr->v.sub[1])); + + case EO_RAW_EQ: case EO_RAW_NE: { + int free_it, ne; + char *value; + + value = eval_raw_value(au, record, expr, &free_it); + if (value == NULL) + return 0; + assert(expr->precomputed_value == 0); + ne = strcmp(expr->v.p.value.string, value); + if (free_it != 0) + free(value); + return expr->op == EO_RAW_EQ ? ne == 0 : ne != 0; + } + + case EO_INTERPRETED_EQ: case EO_INTERPRETED_NE: { + int free_it, ne; + char *value; + + value = eval_interpreted_value(au, record, expr, &free_it); + if (value == NULL) + return 0; + assert(expr->precomputed_value == 0); + ne = strcmp(expr->v.p.value.string, value); + if (free_it != 0) + free(value); + return expr->op == EO_INTERPRETED_EQ ? ne == 0 : ne != 0; + } + + case EO_VALUE_EQ: case EO_VALUE_NE: case EO_VALUE_LT: case EO_VALUE_LE: + case EO_VALUE_GT: case EO_VALUE_GE: { + int err, cmp; + + cmp = compare_values(au, record, expr, &err); + if (err != 0) + return 0; + switch (expr->op) { + case EO_VALUE_EQ: + return cmp == 0; + + case EO_VALUE_NE: + return cmp != 0; + + case EO_VALUE_LT: + return cmp < 0; + + case EO_VALUE_LE: + return cmp <= 0; + + case EO_VALUE_GT: + return cmp > 0; + + case EO_VALUE_GE: + return cmp >= 0; + + default: + abort(); + } + } + + case EO_FIELD_EXISTS: + assert(expr->virtual_field == 0); + nvlist_first(&record->nv); + return nvlist_find_name(&record->nv, expr->v.p.field.name) != 0; + + case EO_REGEXP_MATCHES: + return regexec(expr->v.regexp, record->record, 0, NULL, 0) == 0; + + default: + abort(); + } +} diff --git a/framework/src/audit/auparse/expression.h b/framework/src/audit/auparse/expression.h new file mode 100644 index 00000000..b4af66f0 --- /dev/null +++ b/framework/src/audit/auparse/expression.h @@ -0,0 +1,133 @@ +/* +* expression.h - Expression parsing and handling +* Copyright (C) 2008,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Miloslav Trmač +* Steve Grubb extended timestamp +*/ + +#ifndef EXPRESSION_H__ +#define EXPRESSION_H__ + +#include +#include + +#include "internal.h" + +enum { + EO_NOT, /* Uses v.sub[0] */ + EO_AND, EO_OR, /* Uses v.sub[0] and v.sub[1] */ + /* All of the following use v.p */ + EO_RAW_EQ, EO_RAW_NE, EO_INTERPRETED_EQ, EO_INTERPRETED_NE, + EO_VALUE_EQ, EO_VALUE_NE, EO_VALUE_LT, EO_VALUE_LE, EO_VALUE_GT, + EO_VALUE_GE, + /* Uses v.p.field. Cannot be specified by an expression. */ + EO_FIELD_EXISTS, + EO_REGEXP_MATCHES, /* Uses v.regexp */ + NUM_EO_VALUES, +}; + +enum field_id { + EF_TIMESTAMP, EF_RECORD_TYPE, EF_TIMESTAMP_EX +}; + +struct expr { + unsigned op : 8; /* EO_* */ + unsigned virtual_field : 1; + /* Can be non-zero only if virtual_field != 0 */ + unsigned precomputed_value : 1; + union { + struct expr *sub[2]; + struct { + union { + char *name; + enum field_id id; /* If virtual_field != 0 */ + } field; + union { + char *string; + /* A member from the following is selected + implicitly by field.id. */ + struct { + time_t sec; + unsigned int milli; + } timestamp; /* EF_TIMESTAMP */ + struct { + time_t sec; + unsigned milli; + unsigned serial; + } timestamp_ex; /* EF_TIMESTAMP_EX */ + int int_value; /* EF_RECORD_TYPE */ + } value; + } p; + regex_t *regexp; + } v; +}; + +/* Free EXPR and all its subexpressions. */ +void expr_free(struct expr *expr) hidden; + +/* Parse STRING. + On success, return the parsed expression tree. + On error, set *ERROR to an error string (for free()) or NULL, and return + NULL. (*ERROR == NULL is allowed to handle out-of-memory errors) */ +struct expr *expr_parse(const char *string, char **error) hidden; + +/* Create a comparison-expression for FIELD, OP and VALUE. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_comparison(const char *field, unsigned op, + const char *value) hidden; + +/* Create a timestamp comparison-expression for with OP, SEC, MILLI. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_timestamp_comparison(unsigned op, time_t sec, + unsigned milli) hidden; + +/* Create an extended timestamp comparison-expression for with OP, SEC, + MILLI, and SERIAL. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_timestamp_comparison_ex(unsigned op, time_t sec, + unsigned milli, unsigned serial) hidden; + +/* Create an EO_FIELD_EXISTS-expression for FIELD. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_field_exists(const char *field) hidden; + +/* Create a \regexp expression for regexp comparison. + On success, return the created expression. + On error, set errno and return NULL. */ +struct expr *expr_create_regexp_expression(const char *regexp) hidden; + +/* Create a binary expresion for OP and subexpressions E1 and E2. + On success, return the created expresion. + On error, set errno and return NULL. */ +struct expr *expr_create_binary(unsigned op, struct expr *e1, struct expr *e2) + hidden; + +/* Evaluate EXPR on RECORD in AU->le. + Return 1 if EXPR is true, 0 if it false or if it fails. + (No error reporting facility is provided; an invalid term is considered to + be false; e.g. !invalid is true.) */ +int expr_eval(auparse_state_t *au, rnode *record, const struct expr *expr) + hidden; + +#endif diff --git a/framework/src/audit/auparse/famtab.h b/framework/src/audit/auparse/famtab.h new file mode 100644 index 00000000..31d63079 --- /dev/null +++ b/framework/src/audit/auparse/famtab.h @@ -0,0 +1,62 @@ +/* famtab.h -- + * Copyright 2007,2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/linux/socket.h + */ + +_S(AF_LOCAL, "local" ) +_S(AF_INET, "inet" ) +_S(AF_AX25, "ax25" ) +_S(AF_IPX, "ipx" ) +_S(AF_APPLETALK, "appletalk" ) +_S(AF_NETROM, "netrom" ) +_S(AF_BRIDGE, "bridge" ) +_S(AF_ATMPVC, "atmpvc" ) +_S(AF_X25, "x25" ) +_S(AF_INET6, "inet6" ) +_S(AF_ROSE, "rose" ) +_S(AF_DECnet, "decnet" ) +_S(AF_NETBEUI, "netbeui" ) +_S(AF_SECURITY, "security" ) +_S(AF_KEY, "key" ) +_S(AF_NETLINK, "netlink" ) +_S(AF_PACKET, "packet" ) +_S(AF_ASH, "ash" ) +_S(AF_ECONET, "econet" ) +_S(AF_ATMSVC, "atmsvc" ) +_S(AF_RDS, "rds" ) +_S(AF_SNA, "sna" ) +_S(AF_IRDA, "irda" ) +_S(AF_PPPOX, "pppox" ) +_S(AF_WANPIPE, "wanpipe" ) +_S(AF_LLC, "llc" ) +_S(AF_CAN, "can" ) +_S(AF_TIPC, "tipc" ) +_S(AF_BLUETOOTH, "bluetooth" ) +_S(AF_IUCV, "iucv" ) +_S(AF_RXRPC, "rxrpc" ) +_S(AF_ISDN, "isdn" ) +_S(AF_PHONET, "phonet" ) +_S(AF_IEEE802154, "ieee802154" ) +_S(37, "caif" ) +_S(38, "alg" ) +_S(39, "nfc" ) +_S(40, "vsock" ) + diff --git a/framework/src/audit/auparse/fcntl-cmdtab.h b/framework/src/audit/auparse/fcntl-cmdtab.h new file mode 100644 index 00000000..7e20f92b --- /dev/null +++ b/framework/src/audit/auparse/fcntl-cmdtab.h @@ -0,0 +1,52 @@ +/* fcntl-cmdtab.h -- + * Copyright 2007,2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/asm-generic/fcntl.h <17 + * include/uapi/linux/fcntl.h >= 1024 + */ + +_S(0, "F_DUPFD" ) +_S(1, "F_GETFD" ) +_S(2, "F_SETFD" ) +_S(3, "F_GETFL" ) +_S(4, "F_SETFL" ) +_S(5, "F_GETLK" ) +_S(6, "F_SETLK" ) +_S(7, "F_SETLKW" ) +_S(8, "F_SETOWN" ) +_S(9, "F_GETOWN" ) +_S(10, "F_SETSIG" ) +_S(11, "F_GETSIG" ) +_S(12, "F_GETLK64" ) +_S(13, "F_SETLK64" ) +_S(14, "F_SETLKW64" ) +_S(15, "F_SETOWN_EX" ) +_S(16, "F_GETOWN_EX" ) +_S(17, "F_GETOWNER_UIDS" ) +_S(1024, "F_SETLEASE" ) +_S(1025, "F_GETLEASE" ) +_S(1026, "F_NOTIFY" ) +_S(1029, "F_CANCELLK" ) +_S(1030, "F_DUPFD_CLOEXEC" ) +_S(1031, "F_SETPIPE_SZ" ) +_S(1032, "F_GETPIPE_SZ" ) +_S(1033, "F_ADD_SEALS" ) +_S(1034, "F_GET_SEALS" ) + diff --git a/framework/src/audit/auparse/flagtab.h b/framework/src/audit/auparse/flagtab.h new file mode 100644 index 00000000..7e1146d6 --- /dev/null +++ b/framework/src/audit/auparse/flagtab.h @@ -0,0 +1,33 @@ +/* flagtab.h -- + * Copyright 2007,2012 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: these are only for the RHEL4 kernel + */ + +_S(0x0001, "follow" ) +_S(0x0002, "directory" ) +_S(0x0004, "continue" ) +_S(0x0010, "parent" ) +_S(0x0020, "noalt" ) +_S(0x0040, "atomic" ) +_S(0x0100, "open" ) +_S(0x0200, "create" ) +_S(0x0400, "access" ) + diff --git a/framework/src/audit/auparse/icmptypetab.h b/framework/src/audit/auparse/icmptypetab.h new file mode 100644 index 00000000..a9ee3eef --- /dev/null +++ b/framework/src/audit/auparse/icmptypetab.h @@ -0,0 +1,37 @@ +/* icmptypetab.h -- + * Copyright 2011-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/icmp.h + */ + +_S(0, "echo-reply" ) +_S(3, "destination-unreachable" ) +_S(4, "source-quench" ) +_S(5, "redirect" ) +_S(8, "echo" ) +_S(11, "time-exceeded" ) +_S(12, "parameter-problem" ) +_S(13, "timestamp-request" ) +_S(14, "timestamp-reply" ) +_S(15, "info-request" ) +_S(16, "info-reply" ) +_S(17, "address-mask-request" ) +_S(18, "address-mask-reply" ) + diff --git a/framework/src/audit/auparse/internal.h b/framework/src/audit/auparse/internal.h new file mode 100644 index 00000000..56c0bf9f --- /dev/null +++ b/framework/src/audit/auparse/internal.h @@ -0,0 +1,86 @@ +/* internal.h -- + * Copyright 2006-07,2013-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +#ifndef AUPARSE_INTERNAL_HEADER +#define AUPARSE_INTERNAL_HEADER + +#include "auparse-defs.h" +#include "ellist.h" +#include "auditd-config.h" +#include "data_buf.h" +#include "dso.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is what state the parser is in */ +typedef enum { EVENT_EMPTY, EVENT_ACCUMULATING, EVENT_EMITTED } auparser_state_t; + +/* This is the name/value pair used by search tables */ +struct nv_pair { + int value; + const char *name; +}; + +struct opaque +{ + ausource_t source; // Source type + char **source_list; // Array of buffers, or array of + // file names + int list_idx; // The index into the source list + FILE *in; // If source is file, this is the fd + unsigned int line_number; // line number of current file, zero + // if invalid + char *next_buf; // The current buffer being broken down + unsigned int off; // The current offset into next_buf + char *cur_buf; // The current buffer being parsed + int line_pushed; // True if retrieve_next_line() + // returns same input + event_list_t le; // Linked list of record in same event + struct expr *expr; // Search expression or NULL + char *find_field; // Used to store field name when + // searching + austop_t search_where; // Where to put the cursors on a match + auparser_state_t parse_state; // parsing state + DataBuf databuf; // input data + + // function to call to notify user of parsing changes + void (*callback)(struct opaque *au, auparse_cb_event_t cb_event_type, void *user_data); + + void *callback_user_data; // user data supplied to callback + + // function to call when user_data is destroyed + void (*callback_user_data_destroy)(void *user_data); +}; + +// auditd-config.c +void clear_config(struct daemon_conf *config) hidden; +int load_config(struct daemon_conf *config, log_test_t lt) hidden; +void free_config(struct daemon_conf *config) hidden; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/auparse/interpret.c b/framework/src/audit/auparse/interpret.c new file mode 100644 index 00000000..e8f82f92 --- /dev/null +++ b/framework/src/audit/auparse/interpret.c @@ -0,0 +1,2651 @@ +/* +* interpret.c - Lookup values to something more readable +* Copyright (c) 2007-09,2011-15 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include "nvlist.h" +#include "nvpair.h" +#include "libaudit.h" +#include "internal.h" +#include "interpret.h" +#include "auparse-idata.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // FIXME: remove when ipx.h is fixed +#include +#include +#include +#include +#include +#include "auparse-defs.h" +#include "gen_tables.h" + +#if !HAVE_DECL_ADDR_NO_RANDOMIZE +# define ADDR_NO_RANDOMIZE 0x0040000 +#endif + +/* This is from asm/ipc.h. Copying it for now as some platforms + * have broken headers. */ +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define SEMTIMEDOP 4 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 +#define DIPC 25 + +#include "captabs.h" +#include "clone-flagtabs.h" +#include "epoll_ctls.h" +#include "famtabs.h" +#include "fcntl-cmdtabs.h" +#include "flagtabs.h" +#include "ipctabs.h" +#include "ipccmdtabs.h" +#include "mmaptabs.h" +#include "mounttabs.h" +#include "open-flagtabs.h" +#include "persontabs.h" +#include "prottabs.h" +#include "ptracetabs.h" +#include "recvtabs.h" +#include "rlimittabs.h" +#include "seektabs.h" +#include "socktabs.h" +#include "socktypetabs.h" +#include "signaltabs.h" +#include "clocktabs.h" +#include "typetabs.h" +#include "nfprototabs.h" +#include "icmptypetabs.h" +#include "seccomptabs.h" +#include "accesstabs.h" +#include "prctl_opttabs.h" +#include "schedtabs.h" +#include "shm_modetabs.h" +#include "sockoptnametabs.h" +#include "sockleveltabs.h" +#include "ipoptnametabs.h" +#include "ip6optnametabs.h" +#include "tcpoptnametabs.h" +#include "pktoptnametabs.h" +#include "umounttabs.h" +#include "ioctlreqtabs.h" + +typedef enum { AVC_UNSET, AVC_DENIED, AVC_GRANTED } avc_t; +typedef enum { S_UNSET=-1, S_FAILED, S_SUCCESS } success_t; + +static const char *print_signals(const char *val, unsigned int base); +static auparse_esc_t escape_mode = AUPARSE_ESC_TTY; + +/* + * This function will take a pointer to a 2 byte Ascii character buffer and + * return the actual hex value. + */ +static unsigned char x2c(const unsigned char *buf) +{ + static const char AsciiArray[17] = "0123456789ABCDEF"; + char *ptr; + unsigned char total=0; + + ptr = strchr(AsciiArray, (char)toupper(buf[0])); + if (ptr) + total = (unsigned char)(((ptr-AsciiArray) & 0x0F)<<4); + ptr = strchr(AsciiArray, (char)toupper(buf[1])); + if (ptr) + total += (unsigned char)((ptr-AsciiArray) & 0x0F); + + return total; +} + +// Check if any characters need tty escaping. Returns how many found. +static unsigned int need_tty_escape(const unsigned char *s, unsigned int len) +{ + unsigned int i = 0, cnt = 0; + while (i < len) { + if (s[i] < 32) + cnt++; + i++; + } + return cnt; +} + +// TTY escaping s string into dest. +static void tty_escape(const char *s, char *dest, unsigned int len) +{ + unsigned int i = 0, j = 0; + while (i < len) { + if ((unsigned char)s[i] < 32) { + dest[j++] = ('\\'); + dest[j++] = ('0' + ((s[i] & 0300) >> 6)); + dest[j++] = ('0' + ((s[i] & 0070) >> 3)); + dest[j++] = ('0' + (s[i] & 0007)); + } else + dest[j++] = s[i]; + i++; + } +} + +static const char sh_set[] = "\"'`$\\"; +static unsigned int need_shell_escape(const char *s, unsigned int len) +{ + unsigned int i = 0, cnt = 0; + while (i < len) { + if (s[i] < 32) + cnt++; + else if (strchr(sh_set, s[i])) + cnt++; + i++; + } + return cnt; +} + +static void shell_escape(const char *s, char *dest, unsigned int len) +{ + unsigned int i = 0, j = 0; + while (i < len) { + if ((unsigned char)s[i] < 32) { + dest[j++] = ('\\'); + dest[j++] = ('0' + ((s[i] & 0300) >> 6)); + dest[j++] = ('0' + ((s[i] & 0070) >> 3)); + dest[j++] = ('0' + (s[i] & 0007)); + } else if (strchr(sh_set, s[i])) { + dest[j++] = ('\\'); + dest[j++] = s[i]; + } else + dest[j++] = s[i]; + i++; + } +} + +static const char quote_set[] = ";'\"`#$&*?[]<>{}\\"; +static unsigned int need_shell_quote_escape(const unsigned char *s, unsigned int len) +{ + unsigned int i = 0, cnt = 0; + while (i < len) { + if (s[i] < 32) + cnt++; + else if (strchr(quote_set, s[i])) + cnt++; + i++; + } + return cnt; +} + +static void shell_quote_escape(const char *s, char *dest, unsigned int len) +{ + unsigned int i = 0, j = 0; + while (i < len) { + if ((unsigned char)s[i] < 32) { + dest[j++] = ('\\'); + dest[j++] = ('0' + ((s[i] & 0300) >> 6)); + dest[j++] = ('0' + ((s[i] & 0070) >> 3)); + dest[j++] = ('0' + (s[i] & 0007)); + } else if (strchr(quote_set, s[i])) { + dest[j++] = ('\\'); + dest[j++] = s[i]; + } else + dest[j++] = s[i]; + i++; + } +} + +/* This should return the count of what needs escaping */ +static unsigned int need_escaping(const char *s, unsigned int len) +{ + switch (escape_mode) + { + case AUPARSE_ESC_RAW: + break; + case AUPARSE_ESC_TTY: + return need_tty_escape(s, len); + case AUPARSE_ESC_SHELL: + return need_shell_escape(s, len); + case AUPARSE_ESC_SHELL_QUOTE: + return need_shell_quote_escape(s, len);; + } + return 0; +} + +static void escape(const char *s, char *dest, unsigned int len) +{ + switch (escape_mode) + { + case AUPARSE_ESC_RAW: + return; + case AUPARSE_ESC_TTY: + return tty_escape(s, dest, len); + case AUPARSE_ESC_SHELL: + return shell_escape(s, dest, len); + case AUPARSE_ESC_SHELL_QUOTE: + return shell_quote_escape(s, dest, len); + } +} + +int set_escape_mode(auparse_esc_t mode) +{ + if (mode < 0 || mode > AUPARSE_ESC_SHELL_QUOTE) + return 1; + escape_mode = mode; + return 0; +} +hidden_def(set_escape_mode) + +static int is_hex_string(const char *str) +{ + while (*str) { + if (!isxdigit(*str)) + return 0; + str++; + } + return 1; +} + +/* returns a freshly malloc'ed and converted buffer */ +char *au_unescape(char *buf) +{ + int len, i; + char saved, *str, *ptr = buf; + + /* Find the end of the name */ + if (*ptr == '(') { + ptr = strchr(ptr, ')'); + if (ptr == NULL) + return NULL; + else + ptr++; + } else { + while (isxdigit(*ptr)) + ptr++; + } + saved = *ptr; + *ptr = 0; + str = strdup(buf); + *ptr = saved; + + /* See if its '(null)' from the kernel */ + if (*buf == '(') + return str; + + /* We can get away with this since the buffer is 2 times + * bigger than what we are putting there. + */ + len = strlen(str); + if (len < 2) { + free(str); + return NULL; + } + ptr = str; + for (i=0; iname; + } else { + // Add it to cache + struct passwd *pw; + pw = getpwuid(uid); + if (pw) { + nvpnode nv; + nv.name = strdup(pw->pw_name); + nv.val = uid; + nvpair_append(&uid_nvl, &nv); + name = uid_nvl.cur->name; + } + } + if (name != NULL) + snprintf(buf, size, "%s", name); + else + snprintf(buf, size, "unknown(%d)", uid); + return buf; +} + +void aulookup_destroy_uid_list(void) +{ + if (uid_list_created == 0) + return; + + nvpair_clear(&uid_nvl); + uid_list_created = 0; +} + +static nvpair gid_nvl; +static int gid_list_created=0; +static const char *aulookup_gid(gid_t gid, char *buf, size_t size) +{ + char *name = NULL; + int rc; + + if (gid == -1) { + snprintf(buf, size, "unset"); + return buf; + } + + // Check the cache first + if (gid_list_created == 0) { + nvpair_create(&gid_nvl); + nvpair_clear(&gid_nvl); + gid_list_created = 1; + } + rc = nvpair_find_val(&gid_nvl, gid); + if (rc) { + name = gid_nvl.cur->name; + } else { + // Add it to cache + struct group *gr; + gr = getgrgid(gid); + if (gr) { + nvpnode nv; + nv.name = strdup(gr->gr_name); + nv.val = gid; + nvpair_append(&gid_nvl, &nv); + name = gid_nvl.cur->name; + } + } + if (name != NULL) + snprintf(buf, size, "%s", name); + else + snprintf(buf, size, "unknown(%d)", gid); + return buf; +} + +void aulookup_destroy_gid_list(void) +{ + if (gid_list_created == 0) + return; + + nvpair_clear(&gid_nvl); + gid_list_created = 0; +} + +static const char *print_uid(const char *val, unsigned int base) +{ + int uid; + char name[64]; + + errno = 0; + uid = strtoul(val, NULL, base); + if (errno) { + char *out; + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + return strdup(aulookup_uid(uid, name, sizeof(name))); +} + +static const char *print_gid(const char *val, unsigned int base) +{ + int gid; + char name[64]; + + errno = 0; + gid = strtoul(val, NULL, base); + if (errno) { + char *out; + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + return strdup(aulookup_gid(gid, name, sizeof(name))); +} + +static const char *print_arch(const char *val, unsigned int machine) +{ + const char *ptr; + char *out; + + if (machine > MACH_AARCH64) { + unsigned int ival; + + errno = 0; + ival = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s) ", val) < 0) + out = NULL; + return out; + } + machine = audit_elf_to_machine(ival); + } + if ((int)machine < 0) { + if (asprintf(&out, "unknown elf type(%s)", val) < 0) + out = NULL; + return out; + } + ptr = audit_machine_to_name(machine); + if (ptr) + return strdup(ptr); + else { + if (asprintf(&out, "unknown machine type(%d)", machine) < 0) + out = NULL; + return out; + } +} + +static const char *print_ipccall(const char *val, unsigned int base) +{ + int a0; + char *out; + const char *func = NULL; + + errno = 0; + a0 = strtol(val, NULL, base); + if (errno) { + char *out; + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + func = ipc_i2s(a0); + if (func) + return strdup(func); + else { + if (asprintf(&out, "unknown ipccall(%s)", val) < 0) + out = NULL; + return out; + } +} + +static const char *print_socketcall(const char *val, unsigned int base) +{ + int a0; + char *out; + const char *func = NULL; + + errno = 0; + a0 = strtol(val, NULL, base); + if (errno) { + char *out; + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + func = sock_i2s(a0); + if (func) + return strdup(func); + else { + if (asprintf(&out, "unknown socketcall(%s)", val) < 0) + out = NULL; + return out; + } +} + +static const char *print_syscall(const idata *id) +{ + const char *sys; + char *out; + int machine = id->machine, syscall = id->syscall; + unsigned long long a0 = id->a0; + + if (machine < 0) + machine = audit_detect_machine(); + if (machine < 0) { + out = strdup(id->val); + return out; + } + sys = audit_syscall_to_name(syscall, machine); + if (sys) { + const char *func = NULL; + if (strcmp(sys, "socketcall") == 0) { + if ((int)a0 == a0) + func = sock_i2s(a0); + } else if (strcmp(sys, "ipc") == 0) + if ((int)a0 == a0) + func = ipc_i2s(a0); + if (func) { + if (asprintf(&out, "%s(%s)", sys, func) < 0) + out = NULL; + } else + return strdup(sys); + } else { + if (asprintf(&out, "unknown syscall(%d)", syscall) < 0) + out = NULL; + } + + return out; +} + +static const char *print_exit(const char *val) +{ + long long ival; + char *out; + + errno = 0; + ival = strtoll(val, NULL, 10); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + if (ival < 0) { + if (asprintf(&out, "%lld(%s)", ival, strerror(-ival)) < 0) + out = NULL; + return out; + } + return strdup(val); +} + +static const char *print_escaped(const char *val) +{ + const char *out; + + if (*val == '"') { + char *term; + val++; + term = strchr(val, '"'); + if (term == NULL) + return strdup(" "); + *term = 0; + out = strdup(val); + *term = '"'; + return out; +// FIXME: working here...was trying to detect (null) and handle that +// differently. The other 2 should have " around the file names. +/* } else if (*val == '(') { + char *term; + val++; + term = strchr(val, ' '); + if (term == NULL) + return; + *term = 0; + printf("%s ", val); */ + } else if (val[0] == '0' && val[1] == '0') + out = au_unescape((char *)&val[2]); // Abstract name af_unix + else + out = au_unescape((char *)val); + if (out) + return out; + return strdup(val); // Something is wrong with string, just send as is +} + +static const char *print_proctitle(const char *val) +{ + char *out = (char *)print_escaped(val); + if (*val != '"') { + size_t len = strlen(val) / 2; + const char *end = out + len; + char *ptr = out; + while ((ptr = rawmemchr(ptr, '\0'))) { + if (ptr >= end) + break; + *ptr = ' '; + ptr++; + } + } + return out; +} + +static const char *print_perm(const char *val) +{ + int ival, printed=0; + char buf[32]; + + errno = 0; + ival = strtol(val, NULL, 10); + if (errno) { + char *out; + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + buf[0] = 0; + + /* The kernel treats nothing (0x00) as everything (0x0F) */ + if (ival == 0) + ival = 0x0F; + if (ival & AUDIT_PERM_READ) { + strcat(buf, "read"); + printed = 1; + } + if (ival & AUDIT_PERM_WRITE) { + if (printed) + strcat(buf, ",write"); + else + strcat(buf, "write"); + printed = 1; + } + if (ival & AUDIT_PERM_EXEC) { + if (printed) + strcat(buf, ",exec"); + else + strcat(buf, "exec"); + printed = 1; + } + if (ival & AUDIT_PERM_ATTR) { + if (printed) + strcat(buf, ",attr"); + else + strcat(buf, "attr"); + } + return strdup(buf); +} + +static const char *print_mode(const char *val, unsigned int base) +{ + unsigned int ival; + char *out, buf[48]; + const char *name; + + errno = 0; + ival = strtoul(val, NULL, base); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + // detect the file type + name = audit_ftype_to_name(ival & S_IFMT); + if (name != NULL) + strcpy(buf, name); + else { + unsigned first_ifmt_bit; + + // The lowest-valued "1" bit in S_IFMT + first_ifmt_bit = S_IFMT & ~(S_IFMT - 1); + sprintf(buf, "%03o", (ival & S_IFMT) / first_ifmt_bit); + } + + // check on special bits + if (S_ISUID & ival) + strcat(buf, ",suid"); + if (S_ISGID & ival) + strcat(buf, ",sgid"); + if (S_ISVTX & ival) + strcat(buf, ",sticky"); + + // and the read, write, execute flags in octal + if (asprintf(&out, "%s,%03o", buf, + (S_IRWXU|S_IRWXG|S_IRWXO) & ival) < 0) + out = NULL; + return out; +} + +static const char *print_mode_short_int(unsigned int ival) +{ + char *out, buf[48]; + + // check on special bits + buf[0] = 0; + if (S_ISUID & ival) + strcat(buf, "suid"); + if (S_ISGID & ival) { + if (buf[0]) + strcat(buf, ","); + strcat(buf, "sgid"); + } + if (S_ISVTX & ival) { + if (buf[0]) + strcat(buf, ","); + strcat(buf, "sticky"); + } + + // and the read, write, execute flags in octal + if (buf[0] == 0) { + if (asprintf(&out, "0%03o", + (S_IRWXU|S_IRWXG|S_IRWXO) & ival) < 0) + out = NULL; + } else + if (asprintf(&out, "%s,0%03o", buf, + (S_IRWXU|S_IRWXG|S_IRWXO) & ival) < 0) + out = NULL; + return out; +} + +static const char *print_mode_short(const char *val, int base) +{ + unsigned int ival; + char *out; + + errno = 0; + ival = strtoul(val, NULL, base); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + return print_mode_short_int(ival); +} + +static const char *print_socket_domain(const char *val) +{ + int i; + char *out; + const char *str; + + errno = 0; + i = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + str = fam_i2s(i); + if (str == NULL) { + if (asprintf(&out, "unknown family(0x%s)", val) < 0) + out = NULL; + return out; + } else + return strdup(str); +} + +static const char *print_socket_type(const char *val) +{ + unsigned int type; + char *out; + const char *str; + + errno = 0; + type = 0xFF & strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + str = sock_type_i2s(type); + if (str == NULL) { + if (asprintf(&out, "unknown type(%s)", val) < 0) + out = NULL; + return out; + } else + return strdup(str); +} + +static const char *print_socket_proto(const char *val) +{ + unsigned int proto; + char *out; + struct protoent *p; + + errno = 0; + proto = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + p = getprotobynumber(proto); + if (p == NULL) { + if (asprintf(&out, "unknown proto(%s)", val) < 0) + out = NULL; + return out; + } else + return strdup(p->p_name); +} + +static const char *print_sockaddr(const char *val) +{ + int slen, rc = 0; + const struct sockaddr *saddr; + char name[NI_MAXHOST], serv[NI_MAXSERV]; + const char *host; + char *out = NULL; + const char *str; + + slen = strlen(val)/2; + host = au_unescape((char *)val); + if (host == NULL) { + if (asprintf(&out, "malformed host(%s)", val) < 0) + out = NULL; + return out; + } + saddr = (struct sockaddr *)host; + + + str = fam_i2s(saddr->sa_family); + if (str == NULL) { + if (asprintf(&out, "unknown family(%d)", saddr->sa_family) < 0) + out = NULL; + free((char *)host); + return out; + } + + // Now print address for some families + switch (saddr->sa_family) { + case AF_LOCAL: + { + const struct sockaddr_un *un = + (struct sockaddr_un *)saddr; + if (un->sun_path[0]) + rc = asprintf(&out, "%s %s", str, + un->sun_path); + else // abstract name + rc = asprintf(&out, "%s %.108s", str, + &un->sun_path[1]); + } + break; + case AF_INET: + if (slen < sizeof(struct sockaddr_in)) { + rc = asprintf(&out, "%s sockaddr len too short", + str); + break; + } + slen = sizeof(struct sockaddr_in); + if (getnameinfo(saddr, slen, name, NI_MAXHOST, serv, + NI_MAXSERV, NI_NUMERICHOST | + NI_NUMERICSERV) == 0 ) { + rc = asprintf(&out, "%s host:%s serv:%s", str, + name, serv); + } else + rc = asprintf(&out, "%s (error resolving addr)", + str); + break; + case AF_AX25: + { + const struct sockaddr_ax25 *x = + (struct sockaddr_ax25 *)saddr; + rc = asprintf(&out, "%s call:%c%c%c%c%c%c%c", + str, + x->sax25_call.ax25_call[0], + x->sax25_call.ax25_call[1], + x->sax25_call.ax25_call[2], + x->sax25_call.ax25_call[3], + x->sax25_call.ax25_call[4], + x->sax25_call.ax25_call[5], + x->sax25_call.ax25_call[6]); + } + break; + case AF_IPX: + { + const struct sockaddr_ipx *ip = + (struct sockaddr_ipx *)saddr; + rc = asprintf(&out, "%s port:%d net:%u", str, + ip->sipx_port, ip->sipx_network); + } + break; + case AF_ATMPVC: + { + const struct sockaddr_atmpvc* at = + (struct sockaddr_atmpvc *)saddr; + rc = asprintf(&out, "%s int:%d", str, + at->sap_addr.itf); + } + break; + case AF_X25: + { + const struct sockaddr_x25* x = + (struct sockaddr_x25 *)saddr; + rc = asprintf(&out, "%s addr:%.15s", str, + x->sx25_addr.x25_addr); + } + break; + case AF_INET6: + if (slen < sizeof(struct sockaddr_in6)) { + rc = asprintf(&out, + "%s sockaddr6 len too short", + str); + break; + } + slen = sizeof(struct sockaddr_in6); + if (getnameinfo(saddr, slen, name, NI_MAXHOST, serv, + NI_MAXSERV, NI_NUMERICHOST | + NI_NUMERICSERV) == 0 ) { + rc = asprintf(&out, "%s host:%s serv:%s", str, + name, serv); + } else + rc = asprintf(&out, "%s (error resolving addr)", + str); + break; + case AF_NETLINK: + { + const struct sockaddr_nl *n = + (struct sockaddr_nl *)saddr; + rc = asprintf(&out, "%s pid:%u", str, + n->nl_pid); + } + break; + } + if (rc < 0) + out = NULL; + free((char *)host); + return out; +} + +/* This is only used in the RHEL4 kernel */ +static const char *print_flags(const char *val) +{ + int flags, cnt = 0; + size_t i; + char *out, buf[80]; + + errno = 0; + flags = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + if (flags == 0) { + if (asprintf(&out, "none") < 0) + out = NULL; + return out; + } + buf[0] = 0; + for (i=0; i> 32; + p = buf; + for (i=0; i <= CAP_LAST_CAP; i++) { + if (MASK(i%32) & caps[i/32]) { + const char *s; + if (found) + p = stpcpy(p, ","); + s = cap_i2s(i); + if (s != NULL) + p = stpcpy(p, s); + found = 1; + } + } + if (found == 0) + return strdup("none"); + return strdup(buf); +} + +static const char *print_success(const char *val) +{ + int res; + + if (isdigit(*val)) { + errno = 0; + res = strtoul(val, NULL, 10); + if (errno) { + char *out; + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + return strdup(aulookup_success(res)); + } else + return strdup(val); +} + +static const char *print_open_flags(const char *val) +{ + size_t i; + unsigned int flags; + int cnt = 0; + char *out, buf[178]; + + errno = 0; + flags = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + buf[0] = 0; + if ((flags & O_ACCMODE) == 0) { + // Handle O_RDONLY specially + strcat(buf, "O_RDONLY"); + cnt++; + } + for (i=0; ip_name); + } + + return out; +} + +static const char *print_sock_opt_name(const char *val, int machine) +{ + int opt; + char *out; + const char *s; + + errno = 0; + opt = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + // PPC's tables are different + if ((machine == MACH_PPC64 || machine == MACH_PPC) && + opt >= 16 && opt <= 21) + opt+=100; + + s = sockoptname_i2s(opt); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown sockopt name (0x%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_ip_opt_name(const char *val) +{ + int opt; + char *out; + const char *s; + + errno = 0; + opt = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + s = ipoptname_i2s(opt); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown ipopt name (0x%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_ip6_opt_name(const char *val) +{ + int opt; + char *out; + const char *s; + + errno = 0; + opt = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + s = ip6optname_i2s(opt); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown ip6opt name (0x%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_tcp_opt_name(const char *val) +{ + int opt; + char *out; + const char *s; + + errno = 0; + opt = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + s = tcpoptname_i2s(opt); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown tcpopt name (0x%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_udp_opt_name(const char *val) +{ + int opt; + char *out; + + errno = 0; + opt = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + if (opt == 1) + out = strdup("UDP_CORK"); + else if (opt == 100) + out = strdup("UDP_ENCAP"); + else if (asprintf(&out, "unknown udpopt name (0x%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_pkt_opt_name(const char *val) +{ + int opt; + char *out; + const char *s; + + errno = 0; + opt = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + s = pktoptname_i2s(opt); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown pktopt name (0x%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_shmflags(const char *val) +{ + unsigned int flags, partial, i; + int cnt = 0; + char *out, buf[32]; + + errno = 0; + flags = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + partial = flags & 00003000; + buf[0] = 0; + for (i=0; imachine, syscall = id->syscall; + const char *sys = audit_syscall_to_name(syscall, machine); + if (sys) { + if (*sys == 'r') { + if (strcmp(sys, "rt_sigaction") == 0) + return print_signals(val, 16); + else if (strcmp(sys, "renameat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "readlinkat") == 0) + return print_dirfd(val); + } else if (*sys == 'c') { + if (strcmp(sys, "clone") == 0) + return print_clone_flags(val); + else if (strcmp(sys, "clock_settime") == 0) + return print_clock_id(val); + } else if (*sys == 'p') { + if (strcmp(sys, "personality") == 0) + return print_personality(val); + else if (strcmp(sys, "ptrace") == 0) + return print_ptrace(val); + else if (strcmp(sys, "prctl") == 0) + return print_prctl_opt(val); + } else if (*sys == 'm') { + if (strcmp(sys, "mkdirat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "mknodat") == 0) + return print_dirfd(val); + } else if (*sys == 'f') { + if (strcmp(sys, "fchownat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "futimesat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "fchmodat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "faccessat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "futimensat") == 0) + return print_dirfd(val); + } else if (*sys == 'u') { + if (strcmp(sys, "unshare") == 0) + return print_clone_flags(val); + else if (strcmp(sys, "unlinkat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "utimensat") == 0) + return print_dirfd(val); + } else if (strcmp(sys+1, "etrlimit") == 0) + return print_rlimit(val); + else if (*sys == 's') { + if (strcmp(sys, "setuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setreuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setresuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setfsuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setgid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "setregid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "setresgid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "socket") == 0) + return print_socket_domain(val); + else if (strcmp(sys, "setfsgid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "socketcall") == 0) + return print_socketcall(val, 16); + } + else if (strcmp(sys, "linkat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "newfstatat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "openat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "ipccall") == 0) + return print_ipccall(val, 16); + } + if (asprintf(&out, "0x%s", val) < 0) + out = NULL; + return out; +} + +static const char *print_a1(const char *val, const idata *id) +{ + char *out; + int machine = id->machine, syscall = id->syscall; + const char *sys = audit_syscall_to_name(syscall, machine); + if (sys) { + if (*sys == 'f') { + if (strcmp(sys, "fchmod") == 0) + return print_mode_short(val, 16); + else if (strncmp(sys, "fcntl", 5) == 0) + return print_fcntl_cmd(val); + } else if (*sys == 'c') { + if (strcmp(sys, "chmod") == 0) + return print_mode_short(val, 16); + else if (strstr(sys, "chown")) + return print_uid(val, 16); + else if (strcmp(sys, "creat") == 0) + return print_mode_short(val, 16); + } + if (strcmp(sys+1, "etsockopt") == 0) + return print_sock_opt_level(val); + else if (*sys == 's') { + if (strcmp(sys, "setreuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setresuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setregid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "setresgid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "socket") == 0) + return print_socket_type(val); + else if (strcmp(sys, "setns") == 0) + return print_clone_flags(val); + else if (strcmp(sys, "sched_setscheduler") == 0) + return print_sched(val); + } else if (*sys == 'm') { + if (strcmp(sys, "mkdir") == 0) + return print_mode_short(val, 16); + else if (strcmp(sys, "mknod") == 0) + return print_mode(val, 16); + else if (strcmp(sys, "mq_open") == 0) + return print_open_flags(val); + } + else if (strcmp(sys, "open") == 0) + return print_open_flags(val); + else if (strcmp(sys, "access") == 0) + return print_access(val); + else if (strcmp(sys, "epoll_ctl") == 0) + return print_epoll_ctl(val); + else if (strcmp(sys, "kill") == 0) + return print_signals(val, 16); + else if (strcmp(sys, "prctl") == 0) { + if (id->a0 == PR_CAPBSET_READ || + id->a0 == PR_CAPBSET_DROP) + return print_capabilities(val, 16); + else if (id->a0 == PR_SET_PDEATHSIG) + return print_signals(val, 16); + } + else if (strcmp(sys, "tkill") == 0) + return print_signals(val, 16); + else if (strcmp(sys, "umount2") == 0) + return print_umount(val); + else if (strcmp(sys, "ioctl") == 0) + return print_ioctl_req(val); + } + if (asprintf(&out, "0x%s", val) < 0) + out = NULL; + return out; +} + +static const char *print_a2(const char *val, const idata *id) +{ + char *out; + int machine = id->machine, syscall = id->syscall; + const char *sys = audit_syscall_to_name(syscall, machine); + if (sys) { + if (strncmp(sys, "fcntl", 5) == 0) { + int ival; + + errno = 0; + ival = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", + val) < 0) + out = NULL; + return out; + } + switch (id->a1) + { + case F_SETOWN: + return print_uid(val, 16); + case F_SETFD: + if (ival == FD_CLOEXEC) + return strdup("FD_CLOEXEC"); + /* Fall thru okay. */ + case F_SETFL: + case F_SETLEASE: + case F_GETLEASE: + case F_NOTIFY: + break; + } + } else if (strcmp(sys+1, "etsockopt") == 0) { + if (id->a1 == IPPROTO_IP) + return print_ip_opt_name(val); + else if (id->a1 == SOL_SOCKET) + return print_sock_opt_name(val, machine); + else if (id->a1 == IPPROTO_TCP) + return print_tcp_opt_name(val); + else if (id->a1 == IPPROTO_UDP) + return print_udp_opt_name(val); + else if (id->a1 == IPPROTO_IPV6) + return print_ip6_opt_name(val); + else if (id->a1 == SOL_PACKET) + return print_pkt_opt_name(val); + else + goto normal; + } else if (*sys == 'o') { + if (strcmp(sys, "openat") == 0) + return print_open_flags(val); + if ((strcmp(sys, "open") == 0) && (id->a1 & O_CREAT)) + return print_mode_short(val, 16); + } else if (*sys == 'f') { + if (strcmp(sys, "fchmodat") == 0) + return print_mode_short(val, 16); + else if (strcmp(sys, "faccessat") == 0) + return print_access(val); + } else if (*sys == 's') { + if (strcmp(sys, "setresuid") == 0) + return print_uid(val, 16); + else if (strcmp(sys, "setresgid") == 0) + return print_gid(val, 16); + else if (strcmp(sys, "socket") == 0) + return print_socket_proto(val); + else if (strcmp(sys, "sendmsg") == 0) + return print_recv(val); + else if (strcmp(sys, "shmget") == 0) + return print_shmflags(val); + } else if (*sys == 'm') { + if (strcmp(sys, "mmap") == 0) + return print_prot(val, 1); + else if (strcmp(sys, "mkdirat") == 0) + return print_mode_short(val, 16); + else if (strcmp(sys, "mknodat") == 0) + return print_mode_short(val, 16); + else if (strcmp(sys, "mprotect") == 0) + return print_prot(val, 0); + else if ((strcmp(sys, "mq_open") == 0) && + (id->a1 & O_CREAT)) + return print_mode_short(val, 16); + } else if (*sys == 'r') { + if (strcmp(sys, "recvmsg") == 0) + return print_recv(val); + else if (strcmp(sys, "readlinkat") == 0) + return print_dirfd(val); + } else if (*sys == 'l') { + if (strcmp(sys, "linkat") == 0) + return print_dirfd(val); + else if (strcmp(sys, "lseek") == 0) + return print_seek(val); + } + else if (strstr(sys, "chown")) + return print_gid(val, 16); + else if (strcmp(sys, "tgkill") == 0) + return print_signals(val, 16); + } +normal: + if (asprintf(&out, "0x%s", val) < 0) + out = NULL; + return out; +} + +static const char *print_a3(const char *val, const idata *id) +{ + char *out; + int machine = id->machine, syscall = id->syscall; + const char *sys = audit_syscall_to_name(syscall, machine); + if (sys) { + if (*sys == 'm') { + if (strcmp(sys, "mmap") == 0) + return print_mmap(val); + else if (strcmp(sys, "mount") == 0) + return print_mount(val); + } else if (*sys == 'r') { + if (strcmp(sys, "recv") == 0) + return print_recv(val); + else if (strcmp(sys, "recvfrom") == 0) + return print_recv(val); + else if (strcmp(sys, "recvmmsg") == 0) + return print_recv(val); + } else if (*sys == 's') { + if (strcmp(sys, "send") == 0) + return print_recv(val); + else if (strcmp(sys, "sendto") == 0) + return print_recv(val); + else if (strcmp(sys, "sendmmsg") == 0) + return print_recv(val); + } + } + if (asprintf(&out, "0x%s", val) < 0) + out = NULL; + return out; +} + +static const char *print_signals(const char *val, unsigned int base) +{ + int i; + char *out; + + errno = 0; + i = strtoul(val, NULL, base); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + else if (i < 32) { + const char *s = signal_i2s(i); + if (s != NULL) + return strdup(s); + } + if (asprintf(&out, "unknown signal (%s%s)", + base == 16 ? "0x" : "", val) < 0) + out = NULL; + return out; +} + +static const char *print_nfproto(const char *val) +{ + int proto; + char *out; + const char *s; + + errno = 0; + proto = strtoul(val, NULL, 10); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + s = nfproto_i2s(proto); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown netfilter protocol (%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_icmptype(const char *val) +{ + int icmptype; + char *out; + const char *s; + + errno = 0; + icmptype = strtoul(val, NULL, 10); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + + s = icmptype_i2s(icmptype); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown icmp type (%s)", val) < 0) + out = NULL; + return out; +} + +static const char *print_protocol(const char *val) +{ + int i; + char *out; + + errno = 0; + i = strtoul(val, NULL, 10); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + } else { + struct protoent *p = getprotobynumber(i); + if (p) + out = strdup(p->p_name); + else + out = strdup("undefined protocol"); + } + return out; +} + +static const char *print_addr(const char *val) +{ + char *out = strdup(val); + return out; +} + +static const char *print_list(const char *val) +{ + int i; + char *out; + + errno = 0; + i = strtoul(val, NULL, 10); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + } else + out = strdup(audit_flag_to_name(i)); + return out; +} + +struct string_buf { + char *buf; /* NULL if was ever out of memory */ + size_t allocated; + size_t pos; +}; + +/* Append c to buf. */ +static void append_char(struct string_buf *buf, char c) +{ + if (buf->buf == NULL) + return; + if (buf->pos == buf->allocated) { + char *p; + + buf->allocated *= 2; + p = realloc(buf->buf, buf->allocated); + if (p == NULL) { + free(buf->buf); + buf->buf = NULL; + return; + } + buf->buf = p; + } + buf->buf[buf->pos] = c; + buf->pos++; +} + +/* Represent c as a character within a quoted string, and append it to buf. */ +static void tty_append_printable_char(struct string_buf *buf, unsigned char c) +{ + if (c < 0x20 || c > 0x7E) { + append_char(buf, '\\'); + append_char(buf, '0' + ((c >> 6) & 07)); + append_char(buf, '0' + ((c >> 3) & 07)); + append_char(buf, '0' + (c & 07)); + } else { + if (c == '\\' || c == '"') + append_char(buf, '\\'); + append_char(buf, c); + } +} + +/* Search for a name of a sequence of TTY bytes. + If found, return the name and advance *INPUT. Return NULL otherwise. */ +static const char *tty_find_named_key(unsigned char **input, size_t input_len) +{ + /* NUL-terminated list of (sequence, NUL, name, NUL) entries. + First match wins, even if a longer match were possible later */ + static const unsigned char named_keys[] = +#define E(SEQ, NAME) SEQ "\0" NAME "\0" +#include "tty_named_keys.h" +#undef E + "\0"; + + unsigned char *src; + const unsigned char *nk; + + src = *input; + if (*src >= ' ' && (*src < 0x7F || *src >= 0xA0)) + return NULL; /* Fast path */ + nk = named_keys; + do { + const unsigned char *p; + size_t nk_len; + + p = strchr(nk, '\0'); + nk_len = p - nk; + if (nk_len <= input_len && memcmp(src, nk, nk_len) == 0) { + *input += nk_len; + return p + 1; + } + nk = strchr(p + 1, '\0') + 1; + } while (*nk != '\0'); + return NULL; +} + +static const char *print_tty_data(const char *raw_data) +{ + struct string_buf buf; + int in_printable; + unsigned char *data, *data_pos, *data_end; + + if (!is_hex_string(raw_data)) + return strdup(raw_data); + data = au_unescape((char *)raw_data); + if (data == NULL) + return NULL; + data_end = data + strlen(raw_data) / 2; + + buf.allocated = 10; + buf.buf = malloc(buf.allocated); /* NULL handled in append_char() */ + buf.pos = 0; + in_printable = 0; + data_pos = data; + while (data_pos < data_end) { + /* FIXME: Unicode */ + const char *desc; + + desc = tty_find_named_key(&data_pos, data_end - data_pos); + if (desc != NULL) { + if (in_printable != 0) { + append_char(&buf, '"'); + in_printable = 0; + } + if (buf.pos != 0) + append_char(&buf, ','); + append_char(&buf, '<'); + while (*desc != '\0') { + append_char(&buf, *desc); + desc++; + } + append_char(&buf, '>'); + } else { + if (in_printable == 0) { + if (buf.pos != 0) + append_char(&buf, ','); + append_char(&buf, '"'); + in_printable = 1; + } + tty_append_printable_char(&buf, *data_pos); + data_pos++; + } + } + if (in_printable != 0) + append_char(&buf, '"'); + append_char(&buf, '\0'); + free(data); + return buf.buf; +} + +static const char *print_session(const char *val) +{ + if (strcmp(val, "4294967295") == 0) + return strdup("unset"); + else + return strdup(val); +} + +#define SECCOMP_RET_ACTION 0x7fff0000U +static const char *print_seccomp_code(const char *val) +{ + unsigned long code; + char *out; + const char *s; + + errno = 0; + code = strtoul(val, NULL, 16); + if (errno) { + if (asprintf(&out, "conversion error(%s)", val) < 0) + out = NULL; + return out; + } + s = seccomp_i2s(code & SECCOMP_RET_ACTION); + if (s != NULL) + return strdup(s); + if (asprintf(&out, "unknown seccomp code (%s)", val) < 0) + out = NULL; + return out; +} + +int lookup_type(const char *name) +{ + int i; + + if (type_s2i(name, &i) != 0) + return i; + return AUPARSE_TYPE_UNCLASSIFIED; +} + +const char *interpret(const rnode *r) +{ + const nvlist *nv = &r->nv; + int type; + idata id; + nvnode *n; + const char *out; + + id.machine = r->machine; + id.syscall = r->syscall; + id.a0 = r->a0; + id.a1 = r->a1; + id.name = nvlist_get_cur_name(nv); + id.val = nvlist_get_cur_val(nv); + type = auparse_interp_adjust_type(r->type, id.name, id.val); + + out = auparse_do_interpretation(type, &id); + n = nvlist_get_cur(nv); + n->interp_val = (char *)out; + + return out; +} + +/* + * rtype: the record type + * name: the current field name + * value: the current field value + * Returns: field's internal type is returned + */ +int auparse_interp_adjust_type(int rtype, const char *name, const char *val) +{ + int type; + + /* This set of statements overrides or corrects the detection. + * In almost all cases its a double use of a field. */ + if (rtype == AUDIT_EXECVE && *name == 'a' && strcmp(name, "argc") && + !strstr(name, "_len")) + type = AUPARSE_TYPE_ESCAPED; + else if (rtype == AUDIT_AVC && strcmp(name, "saddr") == 0) + type = AUPARSE_TYPE_UNCLASSIFIED; + else if (rtype == AUDIT_USER_TTY && strcmp(name, "msg") == 0) + type = AUPARSE_TYPE_ESCAPED; + else if (rtype == AUDIT_NETFILTER_PKT && strcmp(name, "saddr") == 0) + type = AUPARSE_TYPE_ADDR; + else if (strcmp(name, "acct") == 0) { + if (val[0] == '"') + type = AUPARSE_TYPE_ESCAPED; + else if (is_hex_string(val)) + type = AUPARSE_TYPE_ESCAPED; + else + type = AUPARSE_TYPE_UNCLASSIFIED; + } else if (rtype == AUDIT_PATH && *name =='f' && + strcmp(name, "flags") == 0) + type = AUPARSE_TYPE_FLAGS; + else if (rtype == AUDIT_MQ_OPEN && strcmp(name, "mode") == 0) + type = AUPARSE_TYPE_MODE_SHORT; + else if (rtype == AUDIT_CRYPTO_KEY_USER && strcmp(name, "fp") == 0) + type = AUPARSE_TYPE_UNCLASSIFIED; + else if ((strcmp(name, "id") == 0) && + (rtype == AUDIT_ADD_GROUP || rtype == AUDIT_GRP_MGMT || + rtype == AUDIT_DEL_GROUP)) + type = AUPARSE_TYPE_GID; + else + type = lookup_type(name); + + return type; +} +hidden_def(auparse_interp_adjust_type) + +const char *auparse_do_interpretation(int type, const idata *id) +{ + const char *out; + switch(type) { + case AUPARSE_TYPE_UID: + out = print_uid(id->val, 10); + break; + case AUPARSE_TYPE_GID: + out = print_gid(id->val, 10); + break; + case AUPARSE_TYPE_SYSCALL: + out = print_syscall(id); + break; + case AUPARSE_TYPE_ARCH: + out = print_arch(id->val, id->machine); + break; + case AUPARSE_TYPE_EXIT: + out = print_exit(id->val); + break; + case AUPARSE_TYPE_ESCAPED: + out = print_escaped(id->val); + break; + case AUPARSE_TYPE_PERM: + out = print_perm(id->val); + break; + case AUPARSE_TYPE_MODE: + out = print_mode(id->val,8); + break; + case AUPARSE_TYPE_MODE_SHORT: + out = print_mode_short(id->val,8); + break; + case AUPARSE_TYPE_SOCKADDR: + out = print_sockaddr(id->val); + break; + case AUPARSE_TYPE_FLAGS: + out = print_flags(id->val); + break; + case AUPARSE_TYPE_PROMISC: + out = print_promiscuous(id->val); + break; + case AUPARSE_TYPE_CAPABILITY: + out = print_capabilities(id->val, 10); + break; + case AUPARSE_TYPE_SUCCESS: + out = print_success(id->val); + break; + case AUPARSE_TYPE_A0: + out = print_a0(id->val, id); + break; + case AUPARSE_TYPE_A1: + out = print_a1(id->val, id); + break; + case AUPARSE_TYPE_A2: + out = print_a2(id->val, id); + break; + case AUPARSE_TYPE_A3: + out = print_a3(id->val, id); + break; + case AUPARSE_TYPE_SIGNAL: + out = print_signals(id->val, 10); + break; + case AUPARSE_TYPE_LIST: + out = print_list(id->val); + break; + case AUPARSE_TYPE_TTY_DATA: + out = print_tty_data(id->val); + break; + case AUPARSE_TYPE_SESSION: + out = print_session(id->val); + break; + case AUPARSE_TYPE_CAP_BITMAP: + out = print_cap_bitmap(id->val); + break; + case AUPARSE_TYPE_NFPROTO: + out = print_nfproto(id->val); + break; + case AUPARSE_TYPE_ICMPTYPE: + out = print_icmptype(id->val); + break; + case AUPARSE_TYPE_PROTOCOL: + out = print_protocol(id->val); + break; + case AUPARSE_TYPE_ADDR: + out = print_addr(id->val); + break; + case AUPARSE_TYPE_PERSONALITY: + out = print_personality(id->val); + break; + case AUPARSE_TYPE_SECCOMP: + out = print_seccomp_code(id->val); + break; + case AUPARSE_TYPE_OFLAG: + out = print_open_flags(id->val); + break; + case AUPARSE_TYPE_MMAP: + out = print_mmap(id->val); + break; + case AUPARSE_TYPE_PROCTITLE: + out = print_proctitle(id->val); + break; + case AUPARSE_TYPE_MAC_LABEL: + case AUPARSE_TYPE_UNCLASSIFIED: + default: + out = strdup(id->val); + break; + } + + if (escape_mode != AUPARSE_ESC_RAW) { + unsigned int len = strlen(out); + unsigned int cnt = need_escaping(out, len); + if (cnt) { + char *dest = malloc(len + 1 + (3*cnt)); + if (dest) + escape(out, dest, len); + free((void *)out); + out = dest; + } + } + return out; +} +hidden_def(auparse_do_interpretation) + diff --git a/framework/src/audit/auparse/interpret.h b/framework/src/audit/auparse/interpret.h new file mode 100644 index 00000000..e546452e --- /dev/null +++ b/framework/src/audit/auparse/interpret.h @@ -0,0 +1,54 @@ +/* interpret.h -- + * Copyright 2007,08 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#ifndef INTERPRET_HEADER +#define INTERPRET_HEADER + +#include "config.h" +#include "private.h" +#include "rnode.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +int lookup_type(const char *name); +const char *interpret(const rnode *r); +void aulookup_destroy_uid_list(void); +void aulookup_destroy_gid_list(void); +char *au_unescape(char *buf); + +/* Make these hidden to prevent conflicts */ +hidden_proto(lookup_type); +hidden_proto(interpret); +hidden_proto(aulookup_destroy_uid_list); +hidden_proto(aulookup_destroy_gid_list); +hidden_proto(au_unescape); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/auparse/ioctlreqtab.h b/framework/src/audit/auparse/ioctlreqtab.h new file mode 100644 index 00000000..a3301e3e --- /dev/null +++ b/framework/src/audit/auparse/ioctlreqtab.h @@ -0,0 +1,54 @@ +/* ioctlreqtab.h -- + * Copyright 2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(0x4B3A, "KDSETMODE" ) +_S(0x4B3B, "KDGETMODE" ) +_S(0x5309, "CDROMEJECT" ) +_S(0x530F, "CDROMEJECT_SW" ) +_S(0x5311, "CDROM_GET_UPC" ) +_S(0x5316, "CDROMSEEK" ) +_S(0x5401, "TCGETS" ) +_S(0x5402, "TCSETS" ) +_S(0x5403, "TCSETSW" ) +_S(0x5404, "TCSETSF" ) +_S(0x5409, "TCSBRK" ) +_S(0x540B, "TCFLSH" ) +_S(0x540E, "TIOCSCTTY" ) +_S(0x540F, "TIOCGPGRP" ) +_S(0x5410, "TIOCSPGRP" ) +_S(0x5413, "TIOCGWINSZ" ) +_S(0x5414, "TIOCSWINSZ" ) +_S(0x541B, "TIOCINQ" ) +_S(0x5421, "FIONBIO" ) +_S(0x8901, "FIOSETOWN" ) +_S(0x8903, "FIOGETOWN" ) +_S(0x8910, "SIOCGIFNAME" ) +_S(0x8927, "SIOCGIFHWADDR" ) +_S(0x8933, "SIOCGIFINDEX" ) +_S(0x89a2, "SIOCBRADDIF" ) +_S(0x40045431, "TIOCSPTLCK" ) // Need a better fix for these +_S(0x80045430, "TIOCGPTN" ) +_S(0x80045431, "TIOCSPTLCK" ) +_S(0xC01C64A3, "DRM_IOCTL_MODE_CURSOR" ) +_S(0xC01864B0, "DRM_IOCTL_MODE_PAGE_FLIP" ) +_S(0xC01864B1, "DRM_IOCTL_MODE_DIRTYFB" ) + diff --git a/framework/src/audit/auparse/ip6optnametab.h b/framework/src/audit/auparse/ip6optnametab.h new file mode 100644 index 00000000..16452af0 --- /dev/null +++ b/framework/src/audit/auparse/ip6optnametab.h @@ -0,0 +1,87 @@ +/* ip6optnametab.h -- + * Copyright 2013-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/in6.h + * include/uapi/linux/netfilter_ipv6/ip6_tables.h + */ + +_S(1, "IPV6_ADDRFORM") +_S(2, "IPV6_2292PKTINFO") +_S(3, "IPV6_2292HOPOPTS") +_S(4, "IPV6_2292DSTOPTS") +_S(5, "IPV6_2292RTHDR") +_S(6, "IPV6_2292PKTOPTIONS") +_S(7, "IPV6_CHECKSUM") +_S(8, "IPV6_2292HOPLIMIT") +_S(9, "IPV6_NEXTHOP") +_S(10, "IPV6_AUTHHDR") +_S(11, "IPV6_FLOWINFO") +_S(16, "IPV6_UNICAST_HOPS") +_S(17, "IPV6_MULTICAST_IF") +_S(18, "IPV6_MULTICAST_HOPS") +_S(19, "IPV6_MULTICAST_LOOP") +_S(20, "IPV6_ADD_MEMBERSHIP") +_S(21, "IPV6_DROP_MEMBERSHIP") +_S(22, "IPV6_ROUTER_ALERT") +_S(23, "IPV6_MTU_DISCOVER") +_S(24, "IPV6_MTU") +_S(25, "IPV6_RECVERR") +_S(26, "IPV6_V6ONLY") +_S(27, "IPV6_JOIN_ANYCAST") +_S(28, "IPV6_LEAVE_ANYCAST") +_S(32, "IPV6_FLOWLABEL_MGR") +_S(33, "IPV6_FLOWINFO_SEND") +_S(34, "IPV6_IPSEC_POLICY") +_S(35, "IPV6_XFRM_POLICY") +_S(42, "MCAST_JOIN_GROUP") +_S(43, "MCAST_BLOCK_SOURCE") +_S(44, "MCAST_UNBLOCK_SOURCE") +_S(45, "MCAST_LEAVE_GROUP") +_S(46, "MCAST_JOIN_SOURCE_GROUP") +_S(47, "MCAST_LEAVE_SOURCE_GROUP") +_S(48, "MCAST_MSFILTER") +_S(49, "IPV6_RECVPKTINFO") +_S(50, "IPV6_PKTINFO") +_S(51, "IPV6_RECVHOPLIMIT") +_S(52, "IPV6_HOPLIMIT") +_S(53, "IPV6_RECVHOPOPTS") +_S(54, "IPV6_HOPOPTS") +_S(55, "IPV6_RTHDRDSTOPTS") +_S(56, "IPV6_RECVRTHDR") +_S(57, "IPV6_RTHDR") +_S(58, "IPV6_RECVDSTOPTS") +_S(59, "IPV6_DSTOPTS") +_S(60, "IPV6_RECVPATHMTU") +_S(61, "IPV6_PATHMTU") +_S(62, "IPV6_DONTFRAG") +_S(63, "IPV6_USE_MIN_MTU") +_S(64, "IP6T_SO_SET_REPLACE") +_S(65, "IP6T_SO_SET_ADD_COUNTERS") +_S(66, "IPV6_RECVTCLASS") +_S(67, "IPV6_TCLASS") +_S(68, "IP6T_SO_GET_REVISION_MATCH") +_S(69, "IP6T_SO_GET_REVISION_TARGET") +_S(72, "IPV6_ADDR_PREFERENCES") +_S(73, "IPV6_MINHOPCOUNT") +_S(74, "IPV6_ORIGDSTADDR") +_S(75, "IPV6_TRANSPARENT") +_S(76, "IPV6_UNICAST_IF") +_S(80, "IP6T_SO_ORIGINAL_DST") + diff --git a/framework/src/audit/auparse/ipccmdtab.h b/framework/src/audit/auparse/ipccmdtab.h new file mode 100644 index 00000000..97c6bc30 --- /dev/null +++ b/framework/src/audit/auparse/ipccmdtab.h @@ -0,0 +1,28 @@ +/* ipccmdtab.h -- + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/ipc.h + */ + + +_S(00001000, "IPC_CREAT" ) +_S(00002000, "IPC_EXCL" ) +_S(00004000, "IPC_NOWAIT" ) + diff --git a/framework/src/audit/auparse/ipctab.h b/framework/src/audit/auparse/ipctab.h new file mode 100644 index 00000000..c30eb20c --- /dev/null +++ b/framework/src/audit/auparse/ipctab.h @@ -0,0 +1,37 @@ +/* ipctab.h -- + * Copyright 2007,2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/ipc.h + */ + + +_S(SEMOP, "semop" ) +_S(SEMGET, "semget" ) +_S(SEMCTL, "semctl" ) +_S(4, "semtimedop" ) +_S(MSGSND, "msgsnd" ) +_S(MSGRCV, "msgrcv" ) +_S(MSGGET, "msgget" ) +_S(MSGCTL, "msgctl" ) +_S(SHMAT, "shmat" ) +_S(SHMDT, "shmdt" ) +_S(SHMGET, "shmget" ) +_S(SHMCTL, "shmctl" ) + diff --git a/framework/src/audit/auparse/ipoptnametab.h b/framework/src/audit/auparse/ipoptnametab.h new file mode 100644 index 00000000..38a9fb80 --- /dev/null +++ b/framework/src/audit/auparse/ipoptnametab.h @@ -0,0 +1,70 @@ +/* ipoptnametab.h -- + * Copyright 2013,2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/in.h + * include/uapi/linux/netfilter_ipv4/ip_tables.h + */ + + +_S(1, "IP_TOS") +_S(2, "IP_TTL") +_S(3, "IP_HDRINCL") +_S(4, "IP_OPTIONS") +_S(5, "IP_ROUTER_ALERT") +_S(6, "IP_RECVOPTS") +_S(7, "IP_RETOPTS") +_S(8, "IP_PKTINFO") +_S(9, "IP_PKTOPTIONS") +_S(10, "IP_MTU_DISCOVER") +_S(11, "IP_RECVERR") +_S(12, "IP_RECVTTL") +_S(14, "IP_MTU") +_S(15, "IP_FREEBIND") +_S(16, "IP_IPSEC_POLICY") +_S(17, "IP_XFRM_POLICY") +_S(18, "IP_PASSSEC") +_S(19, "IP_TRANSPARENT") +_S(20, "IP_ORIGDSTADDR") +_S(21, "IP_MINTTL") +_S(22, "IP_NODEFRAG") +_S(23, "IP_CHECKSUM") +_S(32, "IP_MULTICAST_IF") +_S(33, "IP_MULTICAST_TTL") +_S(34, "IP_MULTICAST_LOOP") +_S(35, "IP_ADD_MEMBERSHIP") +_S(36, "IP_DROP_MEMBERSHIP") +_S(37, "IP_UNBLOCK_SOURCE") +_S(38, "IP_BLOCK_SOURCE") +_S(39, "IP_ADD_SOURCE_MEMBERSHIP") +_S(40, "IP_DROP_SOURCE_MEMBERSHIP") +_S(41, "IP_MSFILTER") +_S(42, "MCAST_JOIN_GROUP") +_S(43, "MCAST_BLOCK_SOURCE") +_S(44, "MCAST_UNBLOCK_SOURCE") +_S(45, "MCAST_LEAVE_GROUP") +_S(46, "MCAST_JOIN_SOURCE_GROUP") +_S(47, "MCAST_LEAVE_SOURCE_GROUP") +_S(48, "MCAST_MSFILTER") +_S(49, "IP_MULTICAST_ALL") +_S(50, "IP_UNICAST_IF") +_S(64, "IPT_SO_SET_REPLACE") +_S(65, "IPT_SO_SET_ADD_COUNTERS") +_S(66, "IPT_SO_GET_REVISION_TARGET") + diff --git a/framework/src/audit/auparse/message.c b/framework/src/audit/auparse/message.c new file mode 100644 index 00000000..45b33c0f --- /dev/null +++ b/framework/src/audit/auparse/message.c @@ -0,0 +1,58 @@ +/* message.c -- + * Copyright 2004, 2005 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include "libaudit.h" +#include "private.h" + +/* The message mode refers to where informational messages go + 0 - stderr, 1 - syslog, 2 - quiet. The default is quiet. */ +static message_t message_mode = MSG_QUIET; +static debug_message_t debug_message = DBG_NO; + +void set_aumessage_mode(message_t mode, debug_message_t debug) +{ + message_mode = mode; + debug_message = debug; +} + +void audit_msg(int priority, const char *fmt, ...) +{ + va_list ap; + + if (message_mode == MSG_QUIET) + return; + + if (priority == LOG_DEBUG && debug_message == DBG_NO) + return; + + va_start(ap, fmt); + if (message_mode == MSG_SYSLOG) + vsyslog(priority, fmt, ap); + else { + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + } + va_end( ap ); +} diff --git a/framework/src/audit/auparse/mmaptab.h b/framework/src/audit/auparse/mmaptab.h new file mode 100644 index 00000000..9bd5ef5a --- /dev/null +++ b/framework/src/audit/auparse/mmaptab.h @@ -0,0 +1,40 @@ +/* mmaptab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/asm-generic/mman.h >0x100 + * include/uapi/asm-generic/mman-common.h < 0x100 + * NOTE: If this is updated, also update interpret.c:print_mmap() + */ + +_S(0x00001, "MAP_SHARED" ) +_S(0x00002, "MAP_PRIVATE" ) +_S(0x00010, "MAP_FIXED" ) +_S(0x00020, "MAP_ANONYMOUS" ) +_S(0x00040, "MAP_32BIT" ) +_S(0x00100, "MAP_GROWSDOWN" ) +_S(0x00800, "MAP_DENYWRITE" ) +_S(0x01000, "MAP_EXECUTABLE" ) +_S(0x02000, "MAP_LOCKED" ) +_S(0x04000, "MAP_NORESERVE" ) +_S(0x08000, "MAP_POPULATE" ) +_S(0x10000, "MAP_NONBLOCK" ) +_S(0x20000, "MAP_STACK" ) +_S(0x40000, "MAP_HUGETLB" ) + diff --git a/framework/src/audit/auparse/mounttab.h b/framework/src/audit/auparse/mounttab.h new file mode 100644 index 00000000..ce98a998 --- /dev/null +++ b/framework/src/audit/auparse/mounttab.h @@ -0,0 +1,53 @@ +/* mounttab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/fs.h + * NOTE: When updating this table, update interpret.c:print_mount() + */ + +_S(MS_RDONLY, "MS_RDONLY") +_S(MS_NOSUID, "MS_NOSUID") +_S(MS_NODEV, "MS_NODEV" ) +_S(MS_NOEXEC, "MS_NOEXEC") +_S(MS_SYNCHRONOUS, "MS_SYNCHRONOUS") +_S(MS_REMOUNT, "MS_REMOUNT") +_S(MS_MANDLOCK, "MS_MANDLOCK") +_S(MS_DIRSYNC, "MS_DIRSYNC") +_S(MS_NOATIME, "MS_NOATIME") +_S(MS_NODIRATIME, "MS_NODIRATIME") +_S(MS_BIND, "MS_BIND") +_S(MS_MOVE, "MS_MOVE") +_S(MS_REC, "MS_REC") +_S(MS_SILENT, "MS_SILENT") +_S(MS_POSIXACL, "MS_POSIXACL") +_S(MS_UNBINDABLE, "MS_UNBINDABLE") +_S(MS_PRIVATE, "MS_PRIVATE") +_S(MS_SLAVE, "MS_SLAVE") +_S(MS_SHARED, "MS_SHARED") +_S(MS_RELATIME, "MS_RELATIME") +_S(MS_KERNMOUNT, "MS_KERNMOUNT") +_S(MS_I_VERSION, "MS_I_VERSION") +_S((1<<24), "MS_STRICTATIME") +_S((1<<27), "MS_SNAP_STABLE") +_S((1<<28), "MS_NOSEC") +_S((1<<29), "MS_BORN") +_S(MS_ACTIVE, "MS_ACTIVE") +_S(MS_NOUSER, "MS_NOUSER") + diff --git a/framework/src/audit/auparse/nfprototab.h b/framework/src/audit/auparse/nfprototab.h new file mode 100644 index 00000000..eab43370 --- /dev/null +++ b/framework/src/audit/auparse/nfprototab.h @@ -0,0 +1,31 @@ +/* nfprototab.h -- + * Copyright 2011-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/netfilter.h + */ + +_S(0, "unspecified" ) +_S(1, "inet" ) +_S(2, "ipv4" ) +_S(3, "arp" ) +_S(7, "bridge" ) +_S(10, "ipv6" ) +_S(12, "decnet" ) + diff --git a/framework/src/audit/auparse/nvlist.c b/framework/src/audit/auparse/nvlist.c new file mode 100644 index 00000000..66e7ff8c --- /dev/null +++ b/framework/src/audit/auparse/nvlist.c @@ -0,0 +1,137 @@ +/* +* nvlist.c - Minimal linked list library for name-value pairs +* Copyright (c) 2006-07 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include "nvlist.h" +#include "interpret.h" +#include "auparse-idata.h" + + +void nvlist_create(nvlist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +static void nvlist_last(nvlist *l) +{ + register nvnode* window; + + if (l->head == NULL) + return; + + window = l->head; + while (window->next) + window = window->next; + l->cur = window; +} + +nvnode *nvlist_next(nvlist *l) +{ + if (l->cur) + l->cur = l->cur->next; + return l->cur; +} + +void nvlist_append(nvlist *l, nvnode *node) +{ + nvnode* newnode = malloc(sizeof(nvnode)); + + newnode->name = node->name; + newnode->val = node->val; + newnode->interp_val = NULL; + newnode->item = l->cnt; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else { // Otherwise add pointer to newnode + if (l->cnt == (l->cur->item+1)) { + l->cur->next = newnode; + } + else { + nvlist_last(l); + l->cur->next = newnode; + } + } + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +/* + * This function will start at current index and scan for a name + */ +int nvlist_find_name(nvlist *l, const char *name) +{ + register nvnode* window = l->cur; + + while (window) { + if (strcmp(window->name, name) == 0) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +extern int interp_adjust_type(int rtype, const char *name, const char *val); +int nvlist_get_cur_type(const rnode *r) +{ + const nvlist *l = &r->nv; + return auparse_interp_adjust_type(r->type, l->cur->name, l->cur->val); +} + +const char *nvlist_interp_cur_val(const rnode *r) +{ + const nvlist *l = &r->nv; + if (l->cur->interp_val) + return l->cur->interp_val; + return interpret(r); +} + +void nvlist_clear(nvlist* l) +{ + nvnode* nextnode; + register nvnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->name); + free(current->val); + free(current->interp_val); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} diff --git a/framework/src/audit/auparse/nvlist.h b/framework/src/audit/auparse/nvlist.h new file mode 100644 index 00000000..2924ddc6 --- /dev/null +++ b/framework/src/audit/auparse/nvlist.h @@ -0,0 +1,51 @@ +/* +* nvlist.h - Header file for nvlist.c +* Copyright (c) 2006-07 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#ifndef NVLIST_HEADER +#define NVLIST_HEADER + +#include "config.h" +#include "private.h" +#include +#include "rnode.h" +#include "ellist.h" + + +void nvlist_create(nvlist *l) hidden; +void nvlist_clear(nvlist* l) hidden; +static inline unsigned int nvlist_get_cnt(nvlist *l) { return l->cnt; } +static inline void nvlist_first(nvlist *l) { l->cur = l->head; } +static inline nvnode *nvlist_get_cur(const nvlist *l) { return l->cur; } +nvnode *nvlist_next(nvlist *l) hidden; +static inline const char *nvlist_get_cur_name(const nvlist *l) {if (l->cur) return l->cur->name; else return NULL;} +static inline const char *nvlist_get_cur_val(const nvlist *l) {if (l->cur) return l->cur->val; else return NULL;} +static inline const char *nvlist_get_cur_val_interp(const nvlist *l) {if (l->cur) return l->cur->interp_val; else return NULL;} +int nvlist_get_cur_type(const rnode *r) hidden; +const char *nvlist_interp_cur_val(const rnode *r) hidden; +void nvlist_append(nvlist *l, nvnode *node) hidden; + +/* Given a numeric index, find that record. */ +int nvlist_find_name(nvlist *l, const char *name) hidden; + +#endif + diff --git a/framework/src/audit/auparse/nvpair.c b/framework/src/audit/auparse/nvpair.c new file mode 100644 index 00000000..467d1546 --- /dev/null +++ b/framework/src/audit/auparse/nvpair.c @@ -0,0 +1,89 @@ +/* +* nvpair.c - Minimal linked list library for name-value pairs +* Copyright (c) 2007-08 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include "nvpair.h" + + +void nvpair_create(nvpair *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +void nvpair_append(nvpair *l, nvpnode *node) +{ + nvpnode* newnode = malloc(sizeof(nvpnode)); + + newnode->name = node->name; + newnode->val = node->val; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else { // Otherwise add pointer to newnode + while (l->cur->next) + l->cur = l->cur->next; + l->cur->next = newnode; + } + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +int nvpair_find_val(nvpair *l, long val) +{ + register nvpnode* window = l->head; + + while (window) { + if (window->val == val) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +void nvpair_clear(nvpair *l) +{ + nvpnode* nextnode; + register nvpnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->name); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + diff --git a/framework/src/audit/auparse/nvpair.h b/framework/src/audit/auparse/nvpair.h new file mode 100644 index 00000000..2ea7f635 --- /dev/null +++ b/framework/src/audit/auparse/nvpair.h @@ -0,0 +1,56 @@ +/* +* nvpair.h - Header file for nvpair.c +* Copyright (c) 2007-08 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Authors: +* Steve Grubb +*/ + +#ifndef NVPAIR_HEADER +#define NVPAIR_HEADER + +#include "config.h" +#include "private.h" +#include + +/* This is the node of the linked list. Any data elements that are + * per item goes here. */ +typedef struct _nvpnode{ + char *name; // The name string + long val; // The value field + struct _nvpnode* next; // Next nvpair node pointer +} nvpnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + nvpnode *head; // List head + nvpnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} nvpair; + +void nvpair_create(nvpair *l) hidden; +static inline void nvpair_first(nvpair *l) { l->cur = l->head; } +static inline nvpnode *nvpair_get_cur(nvpair *l) { return l->cur; } +void nvpair_append(nvpair *l, nvpnode *node) hidden; +void nvpair_clear(nvpair *l) hidden; +int nvpair_find_val(nvpair *l, long val) hidden; + + +#endif + diff --git a/framework/src/audit/auparse/open-flagtab.h b/framework/src/audit/auparse/open-flagtab.h new file mode 100644 index 00000000..42bc9950 --- /dev/null +++ b/framework/src/audit/auparse/open-flagtab.h @@ -0,0 +1,44 @@ +/* open-flagtab.h -- + * Copyright 2007,2012-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/asm-generic/fcntl.h + * NOTE: When updating this table, update interpret.c:print_open_flags() + */ + +// Handled in the code: _S(00, "O_RDONLY" ) +_S(01, "O_WRONLY" ) +_S(02, "O_RDWR" ) +_S(0100, "O_CREAT") +_S(0200, "O_EXCL" ) +_S(0400, "O_NOCTTY" ) +_S(01000, "O_TRUNC" ) +_S(02000, "O_APPEND" ) +_S(04000, "O_NONBLOCK" ) +_S(010000, "O_DSYNC" ) +_S(020000, "O_ASYNC" ) +_S(040000, "O_DIRECT" ) +_S(0200000, "O_DIRECTORY" ) +_S(0400000, "O_NOFOLLOW" ) +_S(01000000, "O_NOATIME" ) +_S(02000000, "O_CLOEXEC") +_S(04000000, "__O_SYNC") +_S(010000000, "O_PATH") +_S(020000000, "__O_TMPFILE") + diff --git a/framework/src/audit/auparse/persontab.h b/framework/src/audit/auparse/persontab.h new file mode 100644 index 00000000..a1957653 --- /dev/null +++ b/framework/src/audit/auparse/persontab.h @@ -0,0 +1,45 @@ +/* persontab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/personality.h + */ + +_S(0x0000, "PER_LINUX") +_S(0x0000 | ADDR_LIMIT_32BIT, "PER_LINUX_32BIT") +_S(0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, "PER_SVR4") +_S(0x0002 | STICKY_TIMEOUTS | SHORT_INODE, "PER_SVR3") +_S(0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS | SHORT_INODE, "PER_SCOSVR3") +_S(0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS, "PER_OSR5") +_S(0x0004 | STICKY_TIMEOUTS | SHORT_INODE, "PER_WYSEV386") +_S(0x0005 | STICKY_TIMEOUTS, "PER_ISCR4") +_S(0x0006, "PER_BSD") +_S(0x0006 | STICKY_TIMEOUTS, "PER_SUNOS") +_S(0x0007 | STICKY_TIMEOUTS | SHORT_INODE, "PER_XENIX") +_S(0x0008, "PER_LINUX32") +_S(0x0008 | ADDR_LIMIT_3GB, "PER_LINUX32_3GB") +_S(0x0009 | STICKY_TIMEOUTS, "PER_IRIX32") +_S(0x000a | STICKY_TIMEOUTS, "PER_IRIXN32") +_S(0x000b | STICKY_TIMEOUTS, "PER_IRIX64") +_S(0x000c, "PER_RISCOS") +_S(0x000d | STICKY_TIMEOUTS, "PER_SOLARIS") +_S(0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, "PER_UW7") +_S(0x000f, "PER_OSF4") +_S(0x0010, "PER_HPUX") + diff --git a/framework/src/audit/auparse/pktoptnametab.h b/framework/src/audit/auparse/pktoptnametab.h new file mode 100644 index 00000000..d532a59d --- /dev/null +++ b/framework/src/audit/auparse/pktoptnametab.h @@ -0,0 +1,43 @@ +/* pktoptnametab.h -- + * Copyright 2013-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/if_packet.h + */ + +_S(1, "PACKET_ADD_MEMBERSHIP") +_S(2, "PACKET_DROP_MEMBERSHIP") +_S(3, "PACKET_RECV_OUTPUT") +_S(5, "PACKET_RX_RING") +_S(6, "PACKET_STATISTICS") +_S(7, "PACKET_COPY_THRESH") +_S(8, "PACKET_AUXDATA") +_S(9, "PACKET_ORIGDEV") +_S(10, "PACKET_VERSION") +_S(11, "PACKET_HDRLEN") +_S(12, "PACKET_RESERVE") +_S(13, "PACKET_TX_RING") +_S(14, "PACKET_LOSS") +_S(15, "PACKET_VNET_HDR") +_S(16, "PACKET_TX_TIMESTAMP") +_S(17, "PACKET_TIMESTAMP") +_S(18, "PACKET_FANOUT") +_S(19, "PACKET_TX_HAS_OFF") +_S(20, "PACKET_QDISC_BYPASS") + diff --git a/framework/src/audit/auparse/prctl-opt-tab.h b/framework/src/audit/auparse/prctl-opt-tab.h new file mode 100644 index 00000000..0285a88d --- /dev/null +++ b/framework/src/audit/auparse/prctl-opt-tab.h @@ -0,0 +1,68 @@ +/* prctl-opt-tab.h -- + * Copyright 2013-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/prctl.h + */ + +_S(1, "PR_SET_PDEATHSIG") +_S(2, "PR_GET_PDEATHSIG") +_S(3, "PR_GET_DUMPABLE") +_S(4, "PR_SET_DUMPABLE") +_S(5, "PR_GET_UNALIGN") +_S(6, "PR_SET_UNALIGN") +_S(7, "PR_GET_KEEPCAPS") +_S(8, "PR_SET_KEEPCAPS") +_S(9, "PR_GET_FPEMU") +_S(10, "PR_SET_FPEMU") +_S(11, "PR_GET_FPEXC") +_S(12, "PR_SET_FPEXC") +_S(13, "PR_GET_TIMING") +_S(14, "PR_SET_TIMING") +_S(15, "PR_SET_NAME") +_S(16, "PR_GET_NAME") +_S(19, "PR_GET_ENDIAN") +_S(20, "PR_SET_ENDIAN") +_S(21, "PR_GET_SECCOMP") +_S(22, "PR_SET_SECCOMP") +_S(23, "PR_CAPBSET_READ") +_S(24, "PR_CAPBSET_DROP") +_S(25, "PR_GET_TSC") +_S(26, "PR_SET_TSC") +_S(27, "PR_GET_SECUREBITS") +_S(28, "PR_SET_SECUREBITS") +_S(29, "PR_SET_TIMERSLACK") +_S(30, "PR_GET_TIMERSLACK") +_S(31, "PR_TASK_PERF_EVENTS_DISABLE") +_S(32, "PR_TASK_PERF_EVENTS_ENABLE") +_S(33, "PR_MCE_KILL") +_S(34, "PR_MCE_KILL_GET") +_S(35, "PR_SET_MM") +_S(36, "PR_SET_CHILD_SUBREAPER") +_S(37, "PR_GET_CHILD_SUBREAPER") +_S(38, "PR_SET_NO_NEW_PRIVS") +_S(39, "PR_GET_NO_NEW_PRIVS") +_S(40, "PR_GET_TID_ADDRESS") +_S(41, "PR_SET_THP_DISABLE") +_S(42, "PR_GET_THP_DISABLE") +_S(43, "PR_MPX_ENABLE_MANAGEMENT") +_S(44, "PR_MPX_DISABLE_MANAGEMENT") +_S(45, "PR_SET_FP_MODE") +_S(46, "PR_GET_FP_MODE") + diff --git a/framework/src/audit/auparse/private.h b/framework/src/audit/auparse/private.h new file mode 100644 index 00000000..c0a0da9c --- /dev/null +++ b/framework/src/audit/auparse/private.h @@ -0,0 +1,54 @@ +/* private.h -- + * Copyright 2007,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +#ifndef _PRIVATE_H_ +#define _PRIVATE_H_ + +#include "auparse.h" +#include "libaudit.h" +#include "dso.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Internal syslog messaging */ +#define audit_msg auparse_msg +#define set_aumessage_mode set_aup_message_mode +void auparse_msg(int priority, const char *fmt, ...) hidden +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))); +#else + ; +#endif +void set_aumessage_mode(message_t mode, debug_message_t debug) hidden; + +char *audit_strsplit_r(char *s, char **savedpp); +char *audit_strsplit(char *s); +hidden_proto(audit_strsplit_r) +hidden_proto(audit_strsplit) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/auparse/prottab.h b/framework/src/audit/auparse/prottab.h new file mode 100644 index 00000000..e0edeb84 --- /dev/null +++ b/framework/src/audit/auparse/prottab.h @@ -0,0 +1,28 @@ +/* prottab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/asm-generic/mman-common.h + */ + +_S(1, "PROT_READ" ) +_S(2, "PROT_WRITE" ) +_S(4, "PROT_EXEC" ) +_S(8, "PROT_SEM" ) + diff --git a/framework/src/audit/auparse/ptracetab.h b/framework/src/audit/auparse/ptracetab.h new file mode 100644 index 00000000..11698ab7 --- /dev/null +++ b/framework/src/audit/auparse/ptracetab.h @@ -0,0 +1,55 @@ +/* ptracetab.h -- + * Copyright 2012-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/ptrace.h + */ + +_S(0, "PTRACE_TRACEME" ) +_S(1, "PTRACE_PEEKTEXT" ) +_S(2, "PTRACE_PEEKDATA" ) +_S(3, "PTRACE_PEEKUSER" ) +_S(4, "PTRACE_POKETEXT" ) +_S(5, "PTRACE_POKEDATA" ) +_S(6, "PTRACE_POKEUSER" ) +_S(7, "PTRACE_CONT" ) +_S(8, "PTRACE_KILL" ) +_S(9, "PTRACE_SINGLESTEP" ) +_S(12, "PTRACE_GETREGS" ) +_S(13, "PTRACE_SETREGS" ) +_S(14, "PTRACE_GETFPREGS" ) +_S(15, "PTRACE_SETFPREGS" ) +_S(16, "PTRACE_ATTACH" ) +_S(17, "PTRACE_DETACH" ) +_S(18, "PTRACE_GETFPXREGS" ) +_S(19, "PTRACE_SETFPXREGS" ) +_S(24, "PTRACE_SYSCALL" ) +_S(0x4200, "PTRACE_SETOPTIONS" ) +_S(0x4201, "PTRACE_GETEVENTMSG" ) +_S(0x4202, "PTRACE_GETSIGINFO" ) +_S(0x4203, "PTRACE_SETSIGINFO" ) +_S(0x4204, "PTRACE_GETREGSET" ) +_S(0x4205, "PTRACE_SETREGSET" ) +_S(0x4206, "PTRACE_SEIZE" ) +_S(0x4207, "PTRACE_INTERRUPT" ) +_S(0x4208, "PTRACE_LISTEN" ) +_S(0x4209, "PTRACE_PEEKSIGINFO" ) +_S(0x420a, "PTRACE_GETSIGMASK" ) +_S(0x420b, "PTRACE_SETSIGMASK" ) + diff --git a/framework/src/audit/auparse/recvtab.h b/framework/src/audit/auparse/recvtab.h new file mode 100644 index 00000000..af201ab9 --- /dev/null +++ b/framework/src/audit/auparse/recvtab.h @@ -0,0 +1,46 @@ +/* recvtab.h -- + * Copyright 2012-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/linux/socket.h + * NOTE: If any update are made, update buffer size in interpret.c:print_recv() + */ + +_S(0x00000001, "MSG_OOB") +_S(0x00000002, "MSG_PEEK") +_S(0x00000004, "MSG_DONTROUTE") +_S(0x00000008, "MSG_CTRUNC") +_S(0x00000010, "MSG_PROXY") +_S(0x00000020, "MSG_TRUNC") +_S(0x00000040, "MSG_DONTWAIT") +_S(0x00000080, "MSG_EOR") +_S(0x00000100, "MSG_WAITALL") +_S(0x00000200, "MSG_FIN") +_S(0x00000400, "MSG_SYN") +_S(0x00000800, "MSG_CONFIRM") +_S(0x00001000, "MSG_RST") +_S(0x00002000, "MSG_ERRQUEUE") +_S(0x00004000, "MSG_NOSIGNAL") +_S(0x00008000, "MSG_MORE") +_S(0x00010000, "MSG_WAITFORONE") +_S(0x00020000, "MSG_SENDPAGE_NOTLAST") +_S(0x20000000, "MSG_FASTOPEN") +_S(0x40000000, "MSG_CMSG_CLOEXEC") +_S(0x80000000, "MSG_CMSG_COMPAT") + diff --git a/framework/src/audit/auparse/rlimittab.h b/framework/src/audit/auparse/rlimittab.h new file mode 100644 index 00000000..3efd22f0 --- /dev/null +++ b/framework/src/audit/auparse/rlimittab.h @@ -0,0 +1,40 @@ +/* rlimittab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/asm-generic/resource.h + */ + + _S(0, "RLIMIT_CPU") + _S(1, "RLIMIT_FSIZE") + _S(2, "RLIMIT_DATA") + _S(3, "RLIMIT_STACK") + _S(4, "RLIMIT_CORE") + _S(5, "RLIMIT_RSS") + _S(6, "RLIMIT_NPROC") + _S(7, "RLIMIT_NOFILE") + _S(8, "RLIMIT_MEMLOCK") + _S(9, "RLIMIT_AS") + _S(10,"RLIMIT_LOCKS") + _S(11,"RLIMIT_SIGPENDING") + _S(12,"RLIMIT_MSGQUEUE") + _S(13,"RLIMIT_NICE") + _S(14,"RLIMIT_RTPRIO") + _S(15,"RLIMIT_RTTIME") + diff --git a/framework/src/audit/auparse/rnode.h b/framework/src/audit/auparse/rnode.h new file mode 100644 index 00000000..2c871c95 --- /dev/null +++ b/framework/src/audit/auparse/rnode.h @@ -0,0 +1,63 @@ + +/* rnode.h -- + * Copyright 2007 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#ifndef RNODE_HEADER +#define RNODE_HEADER + +/* This is the node of the linked list. Any data elements that are + * per item goes here. */ +typedef struct _nvnode{ + char *name; // The name string + char *val; // The value field + char *interp_val; // The value field interpretted + unsigned int item; // Which item of the same event + struct _nvnode* next; // Next nvpair node pointer +} nvnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + nvnode *head; // List head + nvnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} nvlist; + + +/* This is the node of the linked list. Any data elements that are per + * * item goes here. */ +typedef struct _rnode{ + char *record; // The whole unparsed record + int type; // record type (KERNEL, USER, LOGIN, etc) + int machine; // The machine type for the event + int syscall; // The syscall for the event + unsigned long long a0; // arg 0 to the syscall + unsigned long long a1; // arg 1 to the syscall + nvlist nv; // name-value linked list of parsed elements + unsigned int item; // Which item of the same event + int list_idx; // The index into the source list, points to where record was found + unsigned int line_number; // The line number where record was found + struct _rnode* next; // Next record node pointer +} rnode; + +#endif + diff --git a/framework/src/audit/auparse/schedtab.h b/framework/src/audit/auparse/schedtab.h new file mode 100644 index 00000000..90e0e7d5 --- /dev/null +++ b/framework/src/audit/auparse/schedtab.h @@ -0,0 +1,31 @@ +/* schedtab.h -- + * Copyright 2013-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/sched.h + */ + + +_S(0, "SCHED_OTHER" ) +_S(1, "SCHED_FIFO" ) +_S(2, "SCHED_RR" ) +_S(3, "SCHED_BATCH" ) +_S(5, "SCHED_IDLE" ) +_S(6, "SCHED_DEADLINE") + diff --git a/framework/src/audit/auparse/seccomptab.h b/framework/src/audit/auparse/seccomptab.h new file mode 100644 index 00000000..3fd5aff9 --- /dev/null +++ b/framework/src/audit/auparse/seccomptab.h @@ -0,0 +1,30 @@ +/* seccomptab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/seccomp.h + */ + + +_S(0x00000000U, "kill" ) +_S(0x00030000U, "trap" ) +_S(0x00050000U, "errno" ) +_S(0x7ff00000U, "trace" ) +_S(0x7fff0000U, "allow" ) + diff --git a/framework/src/audit/auparse/seektab.h b/framework/src/audit/auparse/seektab.h new file mode 100644 index 00000000..118d5fc6 --- /dev/null +++ b/framework/src/audit/auparse/seektab.h @@ -0,0 +1,29 @@ +/* seektab.h -- + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/fs.h + */ + +_S(0, "SEEK_SET") +_S(1, "SEEK_CUR") +_S(2, "SEEK_END") +_S(3, "SEEK_DATA") +_S(4, "SEEK_HOLE") + diff --git a/framework/src/audit/auparse/shm_modetab.h b/framework/src/audit/auparse/shm_modetab.h new file mode 100644 index 00000000..10b5b108 --- /dev/null +++ b/framework/src/audit/auparse/shm_modetab.h @@ -0,0 +1,29 @@ +/* shm_mode.h -- + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/linux/shm.h + */ + + +_S(00001000, "SHM_DEST" ) +_S(00002000, "SHM_LOCKED" ) +_S(00004000, "SHM_HUGETLB" ) +_S(00010000, "SHM_NORESERVE" ) + diff --git a/framework/src/audit/auparse/signaltab.h b/framework/src/audit/auparse/signaltab.h new file mode 100644 index 00000000..173ad9f2 --- /dev/null +++ b/framework/src/audit/auparse/signaltab.h @@ -0,0 +1,56 @@ +/* signaltab.h -- + * Copyright 2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/asm-generic/signal.h + */ + +_S(0, "SIG0" ) +_S(1, "SIGHUP" ) +_S(2, "SIGINT" ) +_S(3, "SIGQUIT" ) +_S(4, "SIGILL" ) +_S(5, "SIGTRAP" ) +_S(6, "SIGABRT" ) +_S(7, "SIGBUS" ) +_S(8, "SIGFPE" ) +_S(9, "SIGKILL" ) +_S(10, "SIGUSR1" ) +_S(11, "SIGSEGV" ) +_S(12, "SIGUSR2" ) +_S(13, "SIGPIPE" ) +_S(14, "SIGALRM" ) +_S(15, "SIGTERM" ) +_S(16, "SIGSTKFLT" ) +_S(17, "SIGCHLD" ) +_S(18, "SIGCONT" ) +_S(19, "SIGSTOP" ) +_S(20, "SIGTSTP" ) +_S(21, "SIGTTIN" ) +_S(22, "SIGTTOU" ) +_S(23, "SIGURG" ) +_S(24, "SIGXCPU" ) +_S(25, "SIGXFSZ" ) +_S(26, "SIGVTALRM" ) +_S(27, "SIGPROF" ) +_S(28, "SIGWINCH" ) +_S(29, "SIGIO" ) +_S(30, "IGPWR" ) +_S(31, "SIGSYS" ) + diff --git a/framework/src/audit/auparse/sockleveltab.h b/framework/src/audit/auparse/sockleveltab.h new file mode 100644 index 00000000..bf376ade --- /dev/null +++ b/framework/src/audit/auparse/sockleveltab.h @@ -0,0 +1,56 @@ +/* sockleveltab.h -- + * Copyright 2013-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/linux/socket.h + */ + + +_S(0, "SOL_IP") +_S(6, "SOL_TCP") +_S(17, "SOL_UDP") +_S(41, "SOL_IPV6") +_S(58, "SOL_ICMPV6") +_S(132, "SOL_SCTP") +_S(136, "SOL_UDPLITE") +_S(255, "SOL_RAW") +_S(256, "SOL_IPX") +_S(257, "SOL_AX25") +_S(258, "SOL_ATALK") +_S(259, "SOL_NETROM") +_S(260, "SOL_ROSE") +_S(261, "SOL_DECNET") +_S(263, "SOL_PACKET") +_S(264, "SOL_ATM") +_S(265, "SOL_AAL") +_S(266, "SOL_IRDA") +_S(267, "SOL_NETBEUI") +_S(268, "SOL_LLC") +_S(269, "SOL_DCCP") +_S(270, "SOL_NETLINK") +_S(271, "SOL_TIPC") +_S(272, "SOL_RXRPC") +_S(273, "SOL_PPPOL2TP") +_S(274, "SOL_BLUETOOTH") +_S(275, "SOL_PNPIPE") +_S(276, "SOL_RDS") +_S(277, "SOL_IUCV") +_S(278, "SOL_CAIF") +_S(279, "SOL_ALG") +_S(280, "SOL_NFC") diff --git a/framework/src/audit/auparse/sockoptnametab.h b/framework/src/audit/auparse/sockoptnametab.h new file mode 100644 index 00000000..85c6692d --- /dev/null +++ b/framework/src/audit/auparse/sockoptnametab.h @@ -0,0 +1,84 @@ +/* sockoptnametab.h -- + * Copyright 2013-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * File: include/uapi/asm-generic/socket.h + */ + + +_S(1, "SO_DEBUG") +_S(2, "SO_REUSEADDR") +_S(3, "SO_TYPE") +_S(4, "SO_ERROR") +_S(5, "SO_DONTROUTE") +_S(6, "SO_BROADCAST") +_S(7, "SO_SNDBUF") +_S(8, "SO_RCVBUF") +_S(9, "SO_KEEPALIVE") +_S(10, "SO_OOBINLINE") +_S(11, "SO_NO_CHECK") +_S(12, "SO_PRIORITY") +_S(13, "SO_LINGER") +_S(14, "SO_BSDCOMPAT") +_S(15, "SO_REUSEPORT") +_S(16, "SO_PASSCRED") +_S(17, "SO_PEERCRED") +_S(18, "SO_RCVLOWAT") +_S(19, "SO_SNDLOWAT") +_S(20, "SO_RCVTIMEO") +_S(21, "SO_SNDTIMEO") +_S(22, "SO_SECURITY_AUTHENTICATION") +_S(23, "SO_SECURITY_ENCRYPTION_TRANSPORT") +_S(24, "SO_SECURITY_ENCRYPTION_NETWORK") +_S(25, "SO_BINDTODEVICE") +_S(26, "SO_ATTACH_FILTER") +_S(27, "SO_DETACH_FILTER") +_S(28, "SO_PEERNAME") +_S(29, "SO_TIMESTAMP") +_S(30, "SO_ACCEPTCONN") +_S(31, "SO_PEERSEC") +_S(32, "SO_SNDBUFFORCE") +_S(33, "SO_RCVBUFFORCE") +_S(34, "SO_PASSSEC") +_S(35, "SO_TIMESTAMPNS") +_S(36, "SO_MARK") +_S(37, "SO_TIMESTAMPING") +_S(38, "SO_PROTOCOL") +_S(39, "SO_DOMAIN") +_S(40, "SO_RXQ_OVFL") +_S(41, "SO_WIFI_STATUS") +_S(42, "SO_PEEK_OFF") +_S(43, "SO_NOFCS") +_S(44, "SO_LOCK_FILTER") +_S(45, "SO_SELECT_ERR_QUEUE") +_S(46, "SO_BUSY_POLL") +_S(47, "SO_MAX_PACING_RATE") +_S(48, "SO_BPF_EXTENSIONS") +_S(49, "SO_INCOMING_CPU") +_S(50, "SO_ATTACH_BPF") + +// PPC has these different +_S(116, "SO_RCVLOWAT") +_S(117, "SO_SNDLOWAT") +_S(118, "SO_RCVTIMEO") +_S(119, "SO_SNDTIMEO") +_S(120, "SO_PASSCRED") +_S(121, "SO_PEERCRED") + + diff --git a/framework/src/audit/auparse/socktab.h b/framework/src/audit/auparse/socktab.h new file mode 100644 index 00000000..8907b4b3 --- /dev/null +++ b/framework/src/audit/auparse/socktab.h @@ -0,0 +1,44 @@ +/* socktab.h -- + * Copyright 2007,2011-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/net.h + */ + +_S(SYS_SOCKET, "socket" ) +_S(SYS_BIND, "bind" ) +_S(SYS_CONNECT, "connect" ) +_S(SYS_LISTEN, "listen" ) +_S(SYS_ACCEPT, "accept" ) +_S(SYS_GETSOCKNAME, "getsockname" ) +_S(SYS_GETPEERNAME, "getpeername" ) +_S(SYS_SOCKETPAIR, "socketpair" ) +_S(SYS_SEND, "send" ) +_S(SYS_RECV, "recv" ) +_S(SYS_SENDTO, "sendto" ) +_S(SYS_RECVFROM, "recvfrom" ) +_S(SYS_SHUTDOWN, "shutdown" ) +_S(SYS_SETSOCKOPT, "setsockopt" ) +_S(SYS_GETSOCKOPT, "getsockopt" ) +_S(SYS_SENDMSG, "sendmsg" ) +_S(SYS_RECVMSG, "recvmsg" ) +_S(SYS_ACCEPT4, "accept4" ) +_S(19, "recvmmsg" ) +_S(20, "sendmmsg" ) + diff --git a/framework/src/audit/auparse/socktypetab.h b/framework/src/audit/auparse/socktypetab.h new file mode 100644 index 00000000..ec00ecfa --- /dev/null +++ b/framework/src/audit/auparse/socktypetab.h @@ -0,0 +1,31 @@ +/* socktypetab.h -- + * Copyright 2012 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/linux/net.h + */ + +_S(1, "SOCK_STREAM") +_S(2, "SOCK_DGRAM") +_S(3, "SOCK_RAW") +_S(4, "SOCK_RDM") +_S(5, "SOCK_SEQPACKET") +_S(6, "SOCK_DCCP") +_S(10, "SOCK_PACKET") + diff --git a/framework/src/audit/auparse/tcpoptnametab.h b/framework/src/audit/auparse/tcpoptnametab.h new file mode 100644 index 00000000..64e1cbe0 --- /dev/null +++ b/framework/src/audit/auparse/tcpoptnametab.h @@ -0,0 +1,49 @@ +/* tcpoptnametab.h -- + * Copyright 2013-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/uapi/linux/tcp.h + */ + +_S(1, "TCP_NODELAY") +_S(2, "TCP_MAXSEG") +_S(3, "TCP_CORK") +_S(4, "TCP_KEEPIDLE") +_S(5, "TCP_KEEPINTVL") +_S(6, "TCP_KEEPCNT") +_S(7, "TCP_SYNCNT") +_S(8, "TCP_LINGER2") +_S(9, "TCP_DEFER_ACCEPT") +_S(10, "TCP_WINDOW_CLAMP") +_S(11, "TCP_INFO") +_S(12, "TCP_QUICKACK") +_S(13, "TCP_CONGESTION") +_S(14, "TCP_MD5SIG") +_S(15, "TCP_COOKIE_TRANSACTIONS") +_S(16, "TCP_THIN_LINEAR_TIMEOUTS") +_S(17, "TCP_THIN_DUPACK") +_S(18, "TCP_USER_TIMEOUT") +_S(19, "TCP_REPAIR") +_S(20, "TCP_REPAIR_QUEUE") +_S(21, "TCP_QUEUE_SEQ") +_S(22, "TCP_REPAIR_OPTIONS") +_S(23, "TCP_FASTOPEN") +_S(24, "TCP_TIMESTAMP") +_S(25, "TCP_NOTSENT_LOWAT") + diff --git a/framework/src/audit/auparse/test/Makefile.am b/framework/src/audit/auparse/test/Makefile.am new file mode 100644 index 00000000..19793508 --- /dev/null +++ b/framework/src/audit/auparse/test/Makefile.am @@ -0,0 +1,91 @@ +# Makefile.am -- +# Copyright 2006-08,2014-15 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig *.cur +AUTOMAKE_OPTIONS = no-dependencies +check_PROGRAMS = auparse_test +dist_check_SCRIPTS = auparse_test.py +EXTRA_DIST = auparse_test.ref auparse_test.ref.py test.log test2.log + +AM_CPPFLAGS = -I${top_srcdir}/auparse -I${top_srcdir}/lib + +auparse_test_SOURCES = auparse_test.c +auparse_test_LDFLAGS = -static +auparse_test_LDADD = ${top_builddir}/auparse/libauparse.la \ + ${top_builddir}/lib/libaudit.la + +drop_srcdir = sed 's,$(srcdir)/test,test,' + +check: auparse_test + test "$(top_srcdir)" = "$(top_builddir)" || \ + cp $(top_srcdir)/auparse/test/test*.log . + LC_ALL=C \ + ./auparse_test > auparse_test.cur + diff -u $(top_srcdir)/auparse/test/auparse_test.ref auparse_test.cur +if HAVE_PYTHON + cp ${top_builddir}/bindings/swig/python/.libs/_audit.so ${top_builddir}/bindings/swig/python + PYTHONPATH=${top_builddir}/bindings/python/python2/.libs/:${top_builddir}/bindings/swig/python:${top_builddir}/bindings/swig/python/.libs \ + LD_LIBRARY_PATH=${top_builddir}/auparse/.libs \ + srcdir=$(srcdir) $(srcdir)/auparse_test.py \ + | $(drop_srcdir) > auparse_test.cur + diff -u $(top_srcdir)/auparse/test/auparse_test.ref.py auparse_test.cur +endif + echo -e "===================\nAuparse Test Passes\n===================" + +diffcheck: auparse_test + ./auparse_test > auparse_test.cur + diff -u $(srcdir)/auparse_test.ref auparse_test.cur + +memcheck: auparse_test + valgrind --leak-check=yes --show-reachable=yes ./auparse_test + +pycheck: auparse_test.py +if HAVE_PYTHON + PYTHONPATH=${top_builddir}/bindings/python/python2/.libs/:${top_builddir}/bindings/swig/python:${top_builddir}/bindings/swig/python/.libs \ + LD_LIBRARY_PATH=${top_builddir}/auparse/.libs \ + srcdir=$(srcdir) $(srcdir)/auparse_test.py +endif + +pydiffcheck: auparse_test.py +if HAVE_PYTHON + PYTHONPATH=${top_builddir}/bindings/python/python2/.libs/:${top_builddir}/bindings/swig/python:${top_builddir}/bindings/swig/python/.libs \ + LD_LIBRARY_PATH=${top_builddir}/auparse/.libs \ + srcdir=$(srcdir) $(srcdir)/auparse_test.py \ + | $(drop_srcdir) > auparse_test.cur + diff $(srcdir)/auparse_test.ref auparse_test.cur +endif + +pymemcheck: auparse_test.py +if HAVE_PYTHON + PYTHONPATH=${top_builddir}/bindings/python/python2/.libs/:${top_builddir}/bindings/swig/python:${top_builddir}/bindings/swig/python/.libs \ + LD_LIBRARY_PATH=${top_builddir}/auparse/.libs srcdir=$(srcdir) valgrind --leak-check=yes --show-reachable=yes python $(srcdir)/auparse_test.py + +${top_builddir}/bindings/python/build/*/auparse.so: ${top_srcdir}/bindings/python/auparse_python.c + cd ${top_builddir}/bindings/python && make +endif + +clean-generic: + $(RM) *.cur +if HAVE_PYTHON + $(RM) ${top_builddir}/bindings/swig/python/_audit.so +endif + test "$(top_srcdir)" = "$(top_builddir)" || $(RM) test*.log diff --git a/framework/src/audit/auparse/test/auparse_test.c b/framework/src/audit/auparse/test/auparse_test.c new file mode 100644 index 00000000..a6477d41 --- /dev/null +++ b/framework/src/audit/auparse/test/auparse_test.c @@ -0,0 +1,469 @@ +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char *buf[] = { + "type=LOGIN msg=audit(1143146623.787:142): login pid=2027 uid=0 old auid=4294967295 new auid=848\n" + "type=SYSCALL msg=audit(1143146623.875:143): arch=c000003e syscall=188 success=yes exit=0 a0=7fffffa9a9f0 a1=3958d11333 a2=5131f0 a3=20 items=1 pid=2027 auid=848 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=tty3 comm=\"login\" exe=\"/bin/login\" subj=system_u:system_r:local_login_t:s0-s0:c0.c255\n", + + "type=USER_LOGIN msg=audit(1143146623.879:146): user pid=2027 uid=0 auid=848 msg=\'uid=848: exe=\"/bin/login\" (hostname=?, addr=?, terminal=tty3 res=success)\'\n", + + NULL +}; + + +static void walk_test(auparse_state_t *au) +{ + int event_cnt = 1, record_cnt; + + do { + if (auparse_first_record(au) <= 0) { + printf("Error getting first record (%s)\n", + strerror(errno)); + exit(1); + } + printf("event %d has %d records\n", event_cnt, + auparse_get_num_records(au)); + record_cnt = 1; + do { + printf(" record %d of type %d(%s) has %d fields\n", + record_cnt, + auparse_get_type(au), + audit_msg_type_to_name(auparse_get_type(au)), + auparse_get_num_fields(au)); + printf(" line=%d file=%s\n", + auparse_get_line_number(au), + auparse_get_filename(au) ? + auparse_get_filename(au) : "None"); + const au_event_t *e = auparse_get_timestamp(au); + if (e == NULL) { + printf("Error getting timestamp - aborting\n"); + exit(1); + } + printf(" event time: %u.%u:%lu, host=%s\n", + (unsigned)e->sec, + e->milli, e->serial, e->host ? e->host : "?"); + auparse_first_field(au); + do { + printf(" %s=%s (%s)\n", + auparse_get_field_name(au), + auparse_get_field_str(au), + auparse_interpret_field(au)); + } while (auparse_next_field(au) > 0); + printf("\n"); + record_cnt++; + } while(auparse_next_record(au) > 0); + event_cnt++; + } while (auparse_next_event(au) > 0); +} + +void light_test(auparse_state_t *au) +{ + int record_cnt; + + do { + if (auparse_first_record(au) <= 0) { + puts("Error getting first record"); + exit(1); + } + printf("event has %d records\n", auparse_get_num_records(au)); + record_cnt = 1; + do { + printf(" record %d of type %d(%s) has %d fields\n", + record_cnt, + auparse_get_type(au), + audit_msg_type_to_name(auparse_get_type(au)), + auparse_get_num_fields(au)); + printf(" line=%d file=%s\n", + auparse_get_line_number(au), + auparse_get_filename(au) ? + auparse_get_filename(au) : "None"); + const au_event_t *e = auparse_get_timestamp(au); + if (e == NULL) { + printf("Error getting timestamp - aborting\n"); + exit(1); + } + printf(" event time: %u.%u:%lu, host=%s\n", + (unsigned)e->sec, + e->milli, e->serial, + e->host ? e->host : "?"); + printf("\n"); + record_cnt++; + } while(auparse_next_record(au) > 0); + + } while (auparse_next_event(au) > 0); +} + +void simple_search(ausource_t source, austop_t where) +{ + auparse_state_t *au; + const char *val; + + if (source == AUSOURCE_FILE) { + au = auparse_init(AUSOURCE_FILE, "./test.log"); + val = "4294967295"; + } else { + au = auparse_init(AUSOURCE_BUFFER_ARRAY, buf); + val = "848"; + } + if (au == NULL) { + printf("auparse_init error - %s\n", strerror(errno)); + exit(1); + } + if (ausearch_add_item(au, "auid", "=", val, AUSEARCH_RULE_CLEAR)){ + printf("ausearch_add_item error - %s\n", strerror(errno)); + exit(1); + } + if (ausearch_set_stop(au, where)){ + printf("ausearch_set_stop error - %s\n", strerror(errno)); + exit(1); + } + if (ausearch_next_event(au) <= 0) + printf("Error searching for auid - %s\n", strerror(errno)); + else + printf("Found %s = %s\n", auparse_get_field_name(au), + auparse_get_field_str(au)); + auparse_destroy(au); +} + +void compound_search(ausearch_rule_t how) +{ + auparse_state_t *au; + + au = auparse_init(AUSOURCE_FILE, "./test.log"); + if (au == NULL) { + printf("auparse_init error - %s\n", strerror(errno)); + exit(1); + } + if (how == AUSEARCH_RULE_AND) { + if (ausearch_add_item(au, "uid", "=", "0", + AUSEARCH_RULE_CLEAR)){ + printf("ausearch_add_item 1 error - %s\n", + strerror(errno)); + exit(1); + } + if (ausearch_add_item(au, "pid", "=", "13015", how)){ + printf("ausearch_add_item 2 error - %s\n", + strerror(errno)); + exit(1); + } + if (ausearch_add_item(au, "type", "=", "USER_START", how)){ + printf("ausearch_add_item 3 error - %s\n", + strerror(errno)); + exit(1); + } + } else { + if (ausearch_add_item(au, "auid", "=", "42", + AUSEARCH_RULE_CLEAR)){ + printf("ausearch_add_item 4 error - %s\n", + strerror(errno)); + exit(1); + } + // should stop on this one + if (ausearch_add_item(au, "auid", "=", "0", how)){ + printf("ausearch_add_item 5 error - %s\n", + strerror(errno)); + exit(1); + } + if (ausearch_add_item(au, "auid", "=", "500", how)){ + printf("ausearch_add_item 6 error - %s\n", + strerror(errno)); + exit(1); + } + } + if (ausearch_set_stop(au, AUSEARCH_STOP_FIELD)){ + printf("ausearch_set_stop error - %s\n", strerror(errno)); + exit(1); + } + if (ausearch_next_event(au) <= 0) + printf("Error searching for auid - %s\n", strerror(errno)); + else + printf("Found %s = %s\n", auparse_get_field_name(au), + auparse_get_field_str(au)); + auparse_destroy(au); +} + +void regex_search(const char *expr) +{ + auparse_state_t *au; + int rc; + + au = auparse_init(AUSOURCE_BUFFER_ARRAY, buf); + if (au == NULL) { + printf("auparse_init error - %s\n", strerror(errno)); + exit(1); + } + if (ausearch_add_regex(au, expr)){ + printf("ausearch_add_regex error - %s\n", strerror(errno)); + exit(1); + } + if (ausearch_set_stop(au, AUSEARCH_STOP_RECORD)){ + printf("ausearch_set_stop error - %s\n", strerror(errno)); + exit(1); + } + rc = ausearch_next_event(au); + if (rc < 0) + printf("Error searching for %s - %s\n", expr, strerror(errno)); + else if (rc == 0) + printf("Not found\n"); + else + printf("Found %s = %s\n", auparse_get_field_name(au), + auparse_get_field_str(au)); + auparse_destroy(au); +} + +static void auparse_callback(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data) +{ + int *event_cnt = (int *)user_data; + int record_cnt; + + if (cb_event_type == AUPARSE_CB_EVENT_READY) { + if (auparse_first_record(au) <= 0) { + printf("can't get first record\n"); + return; + } + printf("event %d has %d records\n", *event_cnt, + auparse_get_num_records(au)); + record_cnt = 1; + do { + printf(" record %d of type %d(%s) has %d fields\n", + record_cnt, + auparse_get_type(au), + audit_msg_type_to_name(auparse_get_type(au)), + auparse_get_num_fields(au)); + printf(" line=%d file=%s\n", + auparse_get_line_number(au), + auparse_get_filename(au) ? + auparse_get_filename(au) : "None"); + const au_event_t *e = auparse_get_timestamp(au); + if (e == NULL) { + return; + } + printf(" event time: %u.%u:%lu, host=%s\n", + (unsigned)e->sec, + e->milli, e->serial, + e->host ? e->host : "?"); + auparse_first_field(au); + do { + printf(" %s=%s (%s)\n", + auparse_get_field_name(au), + auparse_get_field_str(au), + auparse_interpret_field(au)); + } while (auparse_next_field(au) > 0); + printf("\n"); + record_cnt++; + } while(auparse_next_record(au) > 0); + (*event_cnt)++; + } +} + +int main(void) +{ + //char *files[4] = { "test.log", "test2.log", "test3.log", NULL }; + char *files[3] = { "test.log", "test2.log", NULL }; + setlocale (LC_ALL, ""); + auparse_state_t *au; + + au = auparse_init(AUSOURCE_BUFFER_ARRAY, buf); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + return 1; + } + + printf("Starting Test 1, iterate...\n"); + while (auparse_next_event(au) > 0) { + if (auparse_find_field(au, "auid")) { + printf("%s=%s\n", auparse_get_field_name(au), + auparse_get_field_str(au)); + printf("interp auid=%s\n", auparse_interpret_field(au)); + } else + printf("Error iterating to auid\n"); + } + auparse_reset(au); + while (auparse_next_event(au) > 0) { + if (auparse_find_field(au, "auid")) { + do { + printf("%s=%s\n", auparse_get_field_name(au), + auparse_get_field_str(au)); + printf("interp auid=%s\n", auparse_interpret_field(au)); + } while (auparse_find_field_next(au)); + } else + printf("Error iterating to auid\n"); + } + printf("Test 1 Done\n\n"); + + /* Reset, now lets go to beginning and walk the list manually */ + printf("Starting Test 2, walk events, records, and fields...\n"); + auparse_reset(au); + walk_test(au); + auparse_destroy(au); + printf("Test 2 Done\n\n"); + + /* Reset, now lets go to beginning and walk the list manually */ + printf("Starting Test 3, walk events, records of 1 buffer...\n"); + au = auparse_init(AUSOURCE_BUFFER, buf[1]); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + return 1; + } + light_test(au); + auparse_destroy(au); + printf("Test 3 Done\n\n"); + + printf("Starting Test 4, walk events, records of 1 file...\n"); + au = auparse_init(AUSOURCE_FILE, "./test.log"); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + return 1; + } + walk_test(au); + auparse_destroy(au); + printf("Test 4 Done\n\n"); + + printf("Starting Test 5, walk events, records of 2 files...\n"); + au = auparse_init(AUSOURCE_FILE_ARRAY, files); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + return 1; + } + walk_test(au); + auparse_destroy(au); + printf("Test 5 Done\n\n"); + + printf("Starting Test 6, search...\n"); + au = auparse_init(AUSOURCE_BUFFER_ARRAY, buf); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + return 1; + } + if (ausearch_add_item(au, "auid", "=", "500", AUSEARCH_RULE_CLEAR)){ + printf("Error - %s", strerror(errno)); + return 1; + } + if (ausearch_set_stop(au, AUSEARCH_STOP_EVENT)){ + printf("Error - %s", strerror(errno)); + exit(1); + } + if (ausearch_next_event(au) != 0) { + printf("Error search found something it shouldn't have\n"); + } + puts("auid = 500 not found...which is correct"); + ausearch_clear(au); + auparse_destroy(au); + au = auparse_init(AUSOURCE_BUFFER_ARRAY, buf); + if (ausearch_add_item(au,"auid", "exists", NULL, AUSEARCH_RULE_CLEAR)){ + printf("Error - %s", strerror(errno)); + return 1; + } + if (ausearch_set_stop(au, AUSEARCH_STOP_EVENT)){ + printf("Error - %s", strerror(errno)); + exit(1); + } + if (ausearch_next_event(au) <= 0) { + printf("Error searching for existence of auid\n"); + } + puts("auid exists...which is correct"); + puts("Testing BUFFER_ARRAY, stop on field"); + simple_search(AUSOURCE_BUFFER_ARRAY, AUSEARCH_STOP_FIELD); + puts("Testing BUFFER_ARRAY, stop on record"); + simple_search(AUSOURCE_BUFFER_ARRAY, AUSEARCH_STOP_RECORD); + puts("Testing BUFFER_ARRAY, stop on event"); + simple_search(AUSOURCE_BUFFER_ARRAY, AUSEARCH_STOP_EVENT); + puts("Testing test.log, stop on field"); + simple_search(AUSOURCE_FILE, AUSEARCH_STOP_FIELD); + puts("Testing test.log, stop on record"); + simple_search(AUSOURCE_FILE, AUSEARCH_STOP_RECORD); + puts("Testing test.log, stop on event"); + simple_search(AUSOURCE_FILE, AUSEARCH_STOP_EVENT); + auparse_destroy(au); + printf("Test 6 Done\n\n"); + + printf("Starting Test 7, compound search...\n"); + au = auparse_init(AUSOURCE_BUFFER_ARRAY, buf); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + return 1; + } + compound_search(AUSEARCH_RULE_AND); + compound_search(AUSEARCH_RULE_OR); + auparse_destroy(au); + printf("Test 7 Done\n\n"); + + printf("Starting Test 8, regex search...\n"); + puts("Doing regex match..."); + regex_search("1143146623"); + puts("Doing regex wildcard search..."); + regex_search("11431466.*146"); + printf("Test 8 Done\n\n"); + + /* Note: this should match Test 2 exactly */ + printf("Starting Test 9, buffer feed...\n"); + { + int event_cnt = 1; + size_t len, chunk_len = 3; + const char **cur_buf, *p_beg, *p_end, *p_chunk_beg, + *p_chunk_end; + + au = auparse_init(AUSOURCE_FEED, 0); + auparse_add_callback(au, auparse_callback, &event_cnt, NULL); + for (cur_buf = buf, p_beg = *cur_buf; *cur_buf; + cur_buf++, p_beg = *cur_buf) { + len = strlen(p_beg); + p_end = p_beg + len; + p_chunk_beg = p_beg; + while (p_chunk_beg < p_end) { + p_chunk_end = p_chunk_beg + chunk_len; + if (p_chunk_end > p_end) + p_chunk_end = p_end; + + //fwrite(p_chunk_beg, 1, + // p_chunk_end-p_chunk_beg, stdout); + auparse_feed(au, p_chunk_beg, + p_chunk_end-p_chunk_beg); + p_chunk_beg = p_chunk_end; + } + } + + auparse_flush_feed(au); + auparse_destroy(au); + } + printf("Test 9 Done\n\n"); + + /* Note: this should match Test 4 exactly */ + printf("Starting Test 10, file feed...\n"); + { + int *event_cnt = malloc(sizeof(int)); + size_t len; + char filename[] = "./test.log"; + char buf[4]; + FILE *fp; + + *event_cnt = 1; + au = auparse_init(AUSOURCE_FEED, 0); + auparse_add_callback(au, auparse_callback, event_cnt, free); + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "could not open '%s', %s\n", + filename, strerror(errno)); + return 1; + } + while ((len = fread(buf, 1, sizeof(buf), fp))) { + auparse_feed(au, buf, len); + } + + fclose(fp); + auparse_flush_feed(au); + auparse_destroy(au); + } + printf("Test 10 Done\n\n"); + + puts("Finished non-admin tests\n"); + + return 0; +} + diff --git a/framework/src/audit/auparse/test/auparse_test.py b/framework/src/audit/auparse/test/auparse_test.py new file mode 100755 index 00000000..9d9a5c4d --- /dev/null +++ b/framework/src/audit/auparse/test/auparse_test.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python + +import os +srcdir = os.getenv('srcdir') + +buf = ["type=LOGIN msg=audit(1143146623.787:142): login pid=2027 uid=0 old auid=4294967295 new auid=848\ntype=SYSCALL msg=audit(1143146623.875:143): arch=c000003e syscall=188 success=yes exit=0 a0=7fffffa9a9f0 a1=3958d11333 a2=5131f0 a3=20 items=1 pid=2027 auid=848 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=tty3 comm=\"login\" exe=\"/bin/login\" subj=system_u:system_r:local_login_t:s0-s0:c0.c255\n", +"type=USER_LOGIN msg=audit(1143146623.879:146): user pid=2027 uid=0 auid=848 msg=\'uid=848: exe=\"/bin/login\" (hostname=?, addr=?, terminal=tty3 res=success)\'\n", +] +files = [srcdir + "/test.log", srcdir + "/test2.log"] + +import sys +import time +load_path = '../../bindings/python/build/lib.linux-i686-2.4' +if False: + sys.path.insert(0, load_path) + +import auparse +import audit + +def none_to_null(s): + 'used so output matches C version' + if s is None: + return '(null)' + else: + return s + +def walk_test(au): + event_cnt = 1 + + au.reset() + while True: + if not au.first_record(): + print "Error getting first record" + sys.exit(1) + + print "event %d has %d records" % (event_cnt, au.get_num_records()) + + record_cnt = 1 + while True: + print " record %d of type %d(%s) has %d fields" % \ + (record_cnt, + au.get_type(), audit.audit_msg_type_to_name(au.get_type()), + au.get_num_fields()) + print " line=%d file=%s" % (au.get_line_number(), au.get_filename()) + event = au.get_timestamp() + if event is None: + print "Error getting timestamp - aborting" + sys.exit(1) + + print " event time: %d.%d:%d, host=%s" % (event.sec, event.milli, event.serial, none_to_null(event.host)) + au.first_field() + while True: + print " %s=%s (%s)" % (au.get_field_name(), au.get_field_str(), au.interpret_field()) + if not au.next_field(): break + print + record_cnt += 1 + if not au.next_record(): break + event_cnt += 1 + if not au.parse_next_event(): break + + +def light_test(au): + while True: + if not au.first_record(): + print "Error getting first record" + sys.exit(1) + + print "event has %d records" % (au.get_num_records()) + + record_cnt = 1 + while True: + print " record %d of type %d(%s) has %d fields" % \ + (record_cnt, + au.get_type(), audit.audit_msg_type_to_name(au.get_type()), + au.get_num_fields()) + print " line=%d file=%s" % (au.get_line_number(), au.get_filename()) + event = au.get_timestamp() + if event is None: + print "Error getting timestamp - aborting" + sys.exit(1) + + print " event time: %d.%d:%d, host=%s" % (event.sec, event.milli, event.serial, none_to_null(event.host)) + print + record_cnt += 1 + if not au.next_record(): break + if not au.parse_next_event(): break + +def simple_search(au, source, where): + + if source == auparse.AUSOURCE_FILE: + au = auparse.AuParser(auparse.AUSOURCE_FILE, srcdir + "/test.log"); + val = "4294967295" + else: + au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) + val = "848" + + au.search_add_item("auid", "=", val, auparse.AUSEARCH_RULE_CLEAR) + au.search_set_stop(where) + if not au.search_next_event(): + print "Error searching for auid" + else: + print "Found %s = %s" % (au.get_field_name(), au.get_field_str()) + +def compound_search(au, how): + au = auparse.AuParser(auparse.AUSOURCE_FILE, srcdir + "/test.log"); + if how == auparse.AUSEARCH_RULE_AND: + au.search_add_item("uid", "=", "0", auparse.AUSEARCH_RULE_CLEAR) + au.search_add_item("pid", "=", "13015", how) + au.search_add_item("type", "=", "USER_START", how) + else: + au.search_add_item("auid", "=", "42", auparse.AUSEARCH_RULE_CLEAR) + # should stop on this one + au.search_add_item("auid", "=", "0", how) + au.search_add_item("auid", "=", "500", how) + + au.search_set_stop(auparse.AUSEARCH_STOP_FIELD) + if not au.search_next_event(): + print "Error searching for auid" + else: + print "Found %s = %s" % (au.get_field_name(), au.get_field_str()) + +def feed_callback(au, cb_event_type, event_cnt): + if cb_event_type == auparse.AUPARSE_CB_EVENT_READY: + if not au.first_record(): + print "Error getting first record" + sys.exit(1) + + print "event %d has %d records" % (event_cnt[0], au.get_num_records()) + + record_cnt = 1 + while True: + print " record %d of type %d(%s) has %d fields" % \ + (record_cnt, + au.get_type(), audit.audit_msg_type_to_name(au.get_type()), + au.get_num_fields()) + print " line=%d file=%s" % (au.get_line_number(), au.get_filename()) + event = au.get_timestamp() + if event is None: + print "Error getting timestamp - aborting" + sys.exit(1) + + print " event time: %d.%d:%d, host=%s" % (event.sec, event.milli, event.serial, none_to_null(event.host)) + au.first_field() + while True: + print " %s=%s (%s)" % (au.get_field_name(), au.get_field_str(), au.interpret_field()) + if not au.next_field(): break + print + record_cnt += 1 + if not au.next_record(): break + event_cnt[0] += 1 + +au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) + +print "Starting Test 1, iterate..." +while au.parse_next_event(): + if au.find_field("auid"): + print "%s=%s" % (au.get_field_name(), au.get_field_str()) + print "interp auid=%s" % (au.interpret_field()) + else: + print "Error iterating to auid" +print "Test 1 Done\n" + +# Reset, now lets go to beginning and walk the list manually */ +print "Starting Test 2, walk events, records, and fields..." +au.reset() +walk_test(au) +print "Test 2 Done\n" + +# Reset, now lets go to beginning and walk the list manually */ +print "Starting Test 3, walk events, records of 1 buffer..." +au = auparse.AuParser(auparse.AUSOURCE_BUFFER, buf[1]) +light_test(au); +print "Test 3 Done\n" + +print "Starting Test 4, walk events, records of 1 file..." +au = auparse.AuParser(auparse.AUSOURCE_FILE, srcdir + "/test.log"); +walk_test(au); +print "Test 4 Done\n" + +print "Starting Test 5, walk events, records of 2 files..." +au = auparse.AuParser(auparse.AUSOURCE_FILE_ARRAY, files); +walk_test(au); +print "Test 5 Done\n" + +print "Starting Test 6, search..." +au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) +au.search_add_item("auid", "=", "500", auparse.AUSEARCH_RULE_CLEAR) +au.search_set_stop(auparse.AUSEARCH_STOP_EVENT) +if au.search_next_event(): + print "Error search found something it shouldn't have" +else: + print "auid = 500 not found...which is correct" +au.search_clear() +au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) +#au.search_add_item("auid", "exists", None, auparse.AUSEARCH_RULE_CLEAR) +au.search_add_item("auid", "exists", "", auparse.AUSEARCH_RULE_CLEAR) +au.search_set_stop(auparse.AUSEARCH_STOP_EVENT) +if not au.search_next_event(): + print "Error searching for existence of auid" +print "auid exists...which is correct" +print "Testing BUFFER_ARRAY, stop on field" +simple_search(au, auparse.AUSOURCE_BUFFER_ARRAY, auparse.AUSEARCH_STOP_FIELD) +print "Testing BUFFER_ARRAY, stop on record" +simple_search(au, auparse.AUSOURCE_BUFFER_ARRAY, auparse.AUSEARCH_STOP_RECORD) +print "Testing BUFFER_ARRAY, stop on event" +simple_search(au, auparse.AUSOURCE_BUFFER_ARRAY, auparse.AUSEARCH_STOP_EVENT) +print "Testing test.log, stop on field" +simple_search(au, auparse.AUSOURCE_FILE, auparse.AUSEARCH_STOP_FIELD) +print "Testing test.log, stop on record" +simple_search(au, auparse.AUSOURCE_FILE, auparse.AUSEARCH_STOP_RECORD) +print "Testing test.log, stop on event" +simple_search(au, auparse.AUSOURCE_FILE, auparse.AUSEARCH_STOP_EVENT) +print "Test 6 Done\n" + +print "Starting Test 7, compound search..." +au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) +compound_search(au, auparse.AUSEARCH_RULE_AND) +compound_search(au, auparse.AUSEARCH_RULE_OR) +print "Test 7 Done\n" + +print "Starting Test 8, regex search..." +au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) +print "Doing regex match...\n" +au = auparse.AuParser(auparse.AUSOURCE_BUFFER_ARRAY, buf) +print "Test 8 Done\n" + +# Note: this should match Test 2 exactly +# Note: this should match Test 2 exactly +print "Starting Test 9, buffer feed..." +au = auparse.AuParser(auparse.AUSOURCE_FEED); +event_cnt = 1 +au.add_callback(feed_callback, [event_cnt]) +chunk_len = 3 +for s in buf: + s_len = len(s) + beg = 0 + while beg < s_len: + end = min(s_len, beg + chunk_len) + data = s[beg:end] + beg += chunk_len + au.feed(data) +au.flush_feed() +print "Test 9 Done\n" + +# Note: this should match Test 4 exactly +print "Starting Test 10, file feed..." +au = auparse.AuParser(auparse.AUSOURCE_FEED); +event_cnt = 1 +au.add_callback(feed_callback, [event_cnt]) +f = open(srcdir + "/test.log"); +while True: + data = f.read(4) + if not data: break + au.feed(data) +au.flush_feed() +print "Test 10 Done\n" + +print "Finished non-admin tests\n" + +au = None +sys.exit(0) + diff --git a/framework/src/audit/auparse/test/auparse_test.ref b/framework/src/audit/auparse/test/auparse_test.ref new file mode 100644 index 00000000..6cc399bd --- /dev/null +++ b/framework/src/audit/auparse/test/auparse_test.ref @@ -0,0 +1,803 @@ +Starting Test 1, iterate... +auid=4294967295 +interp auid=unset +auid=848 +interp auid=unknown(848) +auid=848 +interp auid=unknown(848) +auid=4294967295 +interp auid=unset +auid=848 +interp auid=unknown(848) +auid=848 +interp auid=unknown(848) +auid=848 +interp auid=unknown(848) +Test 1 Done + +Starting Test 2, walk events, records, and fields... +event 1 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=1 file=None + event time: 1143146623.787:142, host=? + type=LOGIN (LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=4294967295 (unset) + auid=848 (unknown(848)) + +event 2 has 1 records + record 1 of type 1300(SYSCALL) has 24 fields + line=2 file=None + event time: 1143146623.875:143, host=? + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=188 (setxattr) + success=yes (yes) + exit=0 (0) + a0=7fffffa9a9f0 (0x7fffffa9a9f0) + a1=3958d11333 (0x3958d11333) + a2=5131f0 (0x5131f0) + a3=20 (0x20) + items=1 (1) + pid=2027 (2027) + auid=848 (unknown(848)) + uid=0 (root) + gid=0 (root) + euid=0 (root) + suid=0 (root) + fsuid=0 (root) + egid=0 (root) + sgid=0 (root) + fsgid=0 (root) + tty=tty3 (tty3) + comm="login" (login) + exe="/bin/login" (/bin/login) + subj=system_u:system_r:local_login_t:s0-s0:c0.c255 (system_u:system_r:local_login_t:s0-s0:c0.c255) + +event 3 has 1 records + record 1 of type 1112(USER_LOGIN) has 10 fields + line=3 file=None + event time: 1143146623.879:146, host=? + type=USER_LOGIN (USER_LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=848 (unknown(848)) + uid=848 (unknown(848)) + exe="/bin/login" (/bin/login) + hostname=? (?) + addr=? (?) + terminal=tty3 (tty3) + res=success (success) + +Test 2 Done + +Starting Test 3, walk events, records of 1 buffer... +event has 1 records + record 1 of type 1112(USER_LOGIN) has 10 fields + line=1 file=None + event time: 1143146623.879:146, host=? + +Test 3 Done + +Starting Test 4, walk events, records of 1 file... +event 1 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=./test.log + event time: 1170021493.977:293, host=? + type=AVC (AVC) + seresult=denied (denied) + seperms=read,write (read,write) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=./test.log + event time: 1170021493.977:293, host=? + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=./test.log + event time: 1170021493.977:293, host=? + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=./test.log + event time: 1170021493.977:293, host=? + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 2 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=./test.log + event time: 1170021601.340:294, host=? + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 3 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=./test.log + event time: 1170021601.342:295, host=? + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 4 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=./test.log + event time: 1170021601.343:296, host=? + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 5 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=./test.log + event time: 1170021601.344:297, host=? + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 6 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=./test.log + event time: 1170021601.364:298, host=? + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 7 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=./test.log + event time: 1170021601.366:299, host=? + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +Test 4 Done + +Starting Test 5, walk events, records of 2 files... +event 1 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=test.log + event time: 1170021493.977:293, host=? + type=AVC (AVC) + seresult=denied (denied) + seperms=read,write (read,write) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=test.log + event time: 1170021493.977:293, host=? + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=test.log + event time: 1170021493.977:293, host=? + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=test.log + event time: 1170021493.977:293, host=? + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 2 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=test.log + event time: 1170021601.340:294, host=? + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 3 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=test.log + event time: 1170021601.342:295, host=? + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 4 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=test.log + event time: 1170021601.343:296, host=? + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 5 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=test.log + event time: 1170021601.344:297, host=? + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 6 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=test.log + event time: 1170021601.364:298, host=? + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 7 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=test.log + event time: 1170021601.366:299, host=? + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 8 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=test2.log + event time: 1170021493.977:293, host=? + type=AVC (AVC) + seresult=denied (denied) + seperms=read (read) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=test2.log + event time: 1170021493.977:293, host=? + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=test2.log + event time: 1170021493.977:293, host=? + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=test2.log + event time: 1170021493.977:293, host=? + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 9 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=test2.log + event time: 1170021601.340:294, host=? + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 10 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=test2.log + event time: 1170021601.342:295, host=? + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 11 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=test2.log + event time: 1170021601.343:296, host=? + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 12 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=test2.log + event time: 1170021601.344:297, host=? + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 13 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=test2.log + event time: 1170021601.364:298, host=? + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 14 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=test2.log + event time: 1170021601.366:299, host=? + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +Test 5 Done + +Starting Test 6, search... +auid = 500 not found...which is correct +auid exists...which is correct +Testing BUFFER_ARRAY, stop on field +Found auid = 848 +Testing BUFFER_ARRAY, stop on record +Found type = SYSCALL +Testing BUFFER_ARRAY, stop on event +Found type = SYSCALL +Testing test.log, stop on field +Found auid = 4294967295 +Testing test.log, stop on record +Found type = SYSCALL +Testing test.log, stop on event +Found type = AVC +Test 6 Done + +Starting Test 7, compound search... +Found type = USER_START +Found auid = 0 +Test 7 Done + +Starting Test 8, regex search... +Doing regex match... +Found type = LOGIN +Doing regex wildcard search... +Found type = USER_LOGIN +Test 8 Done + +Starting Test 9, buffer feed... +event 1 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=1 file=None + event time: 1143146623.787:142, host=? + type=LOGIN (LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=4294967295 (unset) + auid=848 (unknown(848)) + +event 2 has 1 records + record 1 of type 1300(SYSCALL) has 24 fields + line=2 file=None + event time: 1143146623.875:143, host=? + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=188 (setxattr) + success=yes (yes) + exit=0 (0) + a0=7fffffa9a9f0 (0x7fffffa9a9f0) + a1=3958d11333 (0x3958d11333) + a2=5131f0 (0x5131f0) + a3=20 (0x20) + items=1 (1) + pid=2027 (2027) + auid=848 (unknown(848)) + uid=0 (root) + gid=0 (root) + euid=0 (root) + suid=0 (root) + fsuid=0 (root) + egid=0 (root) + sgid=0 (root) + fsgid=0 (root) + tty=tty3 (tty3) + comm="login" (login) + exe="/bin/login" (/bin/login) + subj=system_u:system_r:local_login_t:s0-s0:c0.c255 (system_u:system_r:local_login_t:s0-s0:c0.c255) + +event 3 has 1 records + record 1 of type 1112(USER_LOGIN) has 10 fields + line=3 file=None + event time: 1143146623.879:146, host=? + type=USER_LOGIN (USER_LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=848 (unknown(848)) + uid=848 (unknown(848)) + exe="/bin/login" (/bin/login) + hostname=? (?) + addr=? (?) + terminal=tty3 (tty3) + res=success (success) + +Test 9 Done + +Starting Test 10, file feed... +event 1 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=None + event time: 1170021493.977:293, host=? + type=AVC (AVC) + seresult=denied (denied) + seperms=read,write (read,write) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=None + event time: 1170021493.977:293, host=? + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=None + event time: 1170021493.977:293, host=? + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=None + event time: 1170021493.977:293, host=? + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 2 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=None + event time: 1170021601.340:294, host=? + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 3 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=None + event time: 1170021601.342:295, host=? + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 4 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=None + event time: 1170021601.343:296, host=? + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 5 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=None + event time: 1170021601.344:297, host=? + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 6 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=None + event time: 1170021601.364:298, host=? + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 7 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=None + event time: 1170021601.366:299, host=? + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +Test 10 Done + +Finished non-admin tests + diff --git a/framework/src/audit/auparse/test/auparse_test.ref.py b/framework/src/audit/auparse/test/auparse_test.ref.py new file mode 100644 index 00000000..d25e0645 --- /dev/null +++ b/framework/src/audit/auparse/test/auparse_test.ref.py @@ -0,0 +1,793 @@ +Starting Test 1, iterate... +auid=4294967295 +interp auid=unset +auid=848 +interp auid=unknown(848) +auid=848 +interp auid=unknown(848) +Test 1 Done + +Starting Test 2, walk events, records, and fields... +event 1 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=1 file=None + event time: 1143146623.787:142, host=(null) + type=LOGIN (LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=4294967295 (unset) + auid=848 (unknown(848)) + +event 2 has 1 records + record 1 of type 1300(SYSCALL) has 24 fields + line=2 file=None + event time: 1143146623.875:143, host=(null) + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=188 (setxattr) + success=yes (yes) + exit=0 (0) + a0=7fffffa9a9f0 (0x7fffffa9a9f0) + a1=3958d11333 (0x3958d11333) + a2=5131f0 (0x5131f0) + a3=20 (0x20) + items=1 (1) + pid=2027 (2027) + auid=848 (unknown(848)) + uid=0 (root) + gid=0 (root) + euid=0 (root) + suid=0 (root) + fsuid=0 (root) + egid=0 (root) + sgid=0 (root) + fsgid=0 (root) + tty=tty3 (tty3) + comm="login" (login) + exe="/bin/login" (/bin/login) + subj=system_u:system_r:local_login_t:s0-s0:c0.c255 (system_u:system_r:local_login_t:s0-s0:c0.c255) + +event 3 has 1 records + record 1 of type 1112(USER_LOGIN) has 10 fields + line=3 file=None + event time: 1143146623.879:146, host=(null) + type=USER_LOGIN (USER_LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=848 (unknown(848)) + uid=848 (unknown(848)) + exe="/bin/login" (/bin/login) + hostname=? (?) + addr=? (?) + terminal=tty3 (tty3) + res=success (success) + +Test 2 Done + +Starting Test 3, walk events, records of 1 buffer... +event has 1 records + record 1 of type 1112(USER_LOGIN) has 10 fields + line=1 file=None + event time: 1143146623.879:146, host=(null) + +Test 3 Done + +Starting Test 4, walk events, records of 1 file... +event 1 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=test.log + event time: 1170021493.977:293, host=(null) + type=AVC (AVC) + seresult=denied (denied) + seperms=read,write (read,write) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=test.log + event time: 1170021493.977:293, host=(null) + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=test.log + event time: 1170021493.977:293, host=(null) + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=test.log + event time: 1170021493.977:293, host=(null) + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 2 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=test.log + event time: 1170021601.340:294, host=(null) + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 3 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=test.log + event time: 1170021601.342:295, host=(null) + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 4 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=test.log + event time: 1170021601.343:296, host=(null) + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 5 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=test.log + event time: 1170021601.344:297, host=(null) + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 6 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=test.log + event time: 1170021601.364:298, host=(null) + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 7 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=test.log + event time: 1170021601.366:299, host=(null) + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +Test 4 Done + +Starting Test 5, walk events, records of 2 files... +event 1 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=test.log + event time: 1170021493.977:293, host=(null) + type=AVC (AVC) + seresult=denied (denied) + seperms=read,write (read,write) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=test.log + event time: 1170021493.977:293, host=(null) + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=test.log + event time: 1170021493.977:293, host=(null) + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=test.log + event time: 1170021493.977:293, host=(null) + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 2 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=test.log + event time: 1170021601.340:294, host=(null) + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 3 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=test.log + event time: 1170021601.342:295, host=(null) + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 4 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=test.log + event time: 1170021601.343:296, host=(null) + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 5 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=test.log + event time: 1170021601.344:297, host=(null) + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 6 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=test.log + event time: 1170021601.364:298, host=(null) + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 7 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=test.log + event time: 1170021601.366:299, host=(null) + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 8 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=test2.log + event time: 1170021493.977:293, host=(null) + type=AVC (AVC) + seresult=denied (denied) + seperms=read (read) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=test2.log + event time: 1170021493.977:293, host=(null) + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=test2.log + event time: 1170021493.977:293, host=(null) + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=test2.log + event time: 1170021493.977:293, host=(null) + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 9 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=test2.log + event time: 1170021601.340:294, host=(null) + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 10 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=test2.log + event time: 1170021601.342:295, host=(null) + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 11 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=test2.log + event time: 1170021601.343:296, host=(null) + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 12 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=test2.log + event time: 1170021601.344:297, host=(null) + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 13 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=test2.log + event time: 1170021601.364:298, host=(null) + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 14 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=test2.log + event time: 1170021601.366:299, host=(null) + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +Test 5 Done + +Starting Test 6, search... +auid = 500 not found...which is correct +auid exists...which is correct +Testing BUFFER_ARRAY, stop on field +Found auid = 848 +Testing BUFFER_ARRAY, stop on record +Found type = SYSCALL +Testing BUFFER_ARRAY, stop on event +Found type = SYSCALL +Testing test.log, stop on field +Found auid = 4294967295 +Testing test.log, stop on record +Found type = SYSCALL +Testing test.log, stop on event +Found type = AVC +Test 6 Done + +Starting Test 7, compound search... +Found type = USER_START +Found auid = 0 +Test 7 Done + +Starting Test 8, regex search... +Doing regex match... + +Test 8 Done + +Starting Test 9, buffer feed... +event 1 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=1 file=None + event time: 1143146623.787:142, host=(null) + type=LOGIN (LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=4294967295 (unset) + auid=848 (unknown(848)) + +event 2 has 1 records + record 1 of type 1300(SYSCALL) has 24 fields + line=2 file=None + event time: 1143146623.875:143, host=(null) + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=188 (setxattr) + success=yes (yes) + exit=0 (0) + a0=7fffffa9a9f0 (0x7fffffa9a9f0) + a1=3958d11333 (0x3958d11333) + a2=5131f0 (0x5131f0) + a3=20 (0x20) + items=1 (1) + pid=2027 (2027) + auid=848 (unknown(848)) + uid=0 (root) + gid=0 (root) + euid=0 (root) + suid=0 (root) + fsuid=0 (root) + egid=0 (root) + sgid=0 (root) + fsgid=0 (root) + tty=tty3 (tty3) + comm="login" (login) + exe="/bin/login" (/bin/login) + subj=system_u:system_r:local_login_t:s0-s0:c0.c255 (system_u:system_r:local_login_t:s0-s0:c0.c255) + +event 3 has 1 records + record 1 of type 1112(USER_LOGIN) has 10 fields + line=3 file=None + event time: 1143146623.879:146, host=(null) + type=USER_LOGIN (USER_LOGIN) + pid=2027 (2027) + uid=0 (root) + auid=848 (unknown(848)) + uid=848 (unknown(848)) + exe="/bin/login" (/bin/login) + hostname=? (?) + addr=? (?) + terminal=tty3 (tty3) + res=success (success) + +Test 9 Done + +Starting Test 10, file feed... +event 1 has 4 records + record 1 of type 1400(AVC) has 11 fields + line=1 file=None + event time: 1170021493.977:293, host=(null) + type=AVC (AVC) + seresult=denied (denied) + seperms=read,write (read,write) + pid=13010 (13010) + comm="pickup" (pickup) + name="maildrop" (maildrop) + dev=hda7 (hda7) + ino=14911367 (14911367) + scontext=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + tclass=dir (dir) + + record 2 of type 1300(SYSCALL) has 26 fields + line=2 file=None + event time: 1170021493.977:293, host=(null) + type=SYSCALL (SYSCALL) + arch=c000003e (x86_64) + syscall=2 (open) + success=no (no) + exit=-13 (-13(Permission denied)) + a0=5555665d91b0 (0x5555665d91b0) + a1=10800 (O_RDONLY|O_NONBLOCK|O_DIRECTORY) + a2=5555665d91b8 (0x5555665d91b8) + a3=0 (0x0) + items=1 (1) + ppid=2013 (2013) + pid=13010 (13010) + auid=4294967295 (unset) + uid=890 (unknown(890)) + gid=890 (unknown(890)) + euid=890 (unknown(890)) + suid=890 (unknown(890)) + fsuid=890 (unknown(890)) + egid=890 (unknown(890)) + sgid=890 (unknown(890)) + fsgid=890 (unknown(890)) + tty=(none) ((none)) + comm="pickup" (pickup) + exe="/usr/libexec/postfix/pickup" (/usr/libexec/postfix/pickup) + subj=system_u:system_r:postfix_pickup_t:s0 (system_u:system_r:postfix_pickup_t:s0) + key=(null) ((null)) + + record 3 of type 1307(CWD) has 2 fields + line=3 file=None + event time: 1170021493.977:293, host=(null) + type=CWD (CWD) + cwd="/var/spool/postfix" (/var/spool/postfix) + + record 4 of type 1302(PATH) has 10 fields + line=4 file=None + event time: 1170021493.977:293, host=(null) + type=PATH (PATH) + item=0 (0) + name="maildrop" (maildrop) + inode=14911367 (14911367) + dev=03:07 (03:07) + mode=040730 (dir,730) + ouid=890 (unknown(890)) + ogid=891 (unknown(891)) + rdev=00:00 (00:00) + obj=system_u:object_r:postfix_spool_maildrop_t:s0 (system_u:object_r:postfix_spool_maildrop_t:s0) + +event 2 has 1 records + record 1 of type 1101(USER_ACCT) has 11 fields + line=5 file=None + event time: 1170021601.340:294, host=(null) + type=USER_ACCT (USER_ACCT) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 3 has 1 records + record 1 of type 1103(CRED_ACQ) has 11 fields + line=6 file=None + event time: 1170021601.342:295, host=(null) + type=CRED_ACQ (CRED_ACQ) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 4 has 1 records + record 1 of type 1006(LOGIN) has 5 fields + line=7 file=None + event time: 1170021601.343:296, host=(null) + type=LOGIN (LOGIN) + pid=13015 (13015) + uid=0 (root) + auid=4294967295 (unset) + auid=0 (root) + +event 5 has 1 records + record 1 of type 1105(USER_START) has 11 fields + line=8 file=None + event time: 1170021601.344:297, host=(null) + type=USER_START (USER_START) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 6 has 1 records + record 1 of type 1104(CRED_DISP) has 11 fields + line=9 file=None + event time: 1170021601.364:298, host=(null) + type=CRED_DISP (CRED_DISP) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +event 7 has 1 records + record 1 of type 1106(USER_END) has 11 fields + line=10 file=None + event time: 1170021601.366:299, host=(null) + type=USER_END (USER_END) + pid=13015 (13015) + uid=0 (root) + auid=0 (root) + subj=system_u:system_r:crond_t:s0-s0:c0.c1023 (system_u:system_r:crond_t:s0-s0:c0.c1023) + acct=root (root) + exe="/usr/sbin/crond" (/usr/sbin/crond) + hostname=? (?) + addr=? (?) + terminal=cron (cron) + res=success (success) + +Test 10 Done + +Finished non-admin tests + diff --git a/framework/src/audit/auparse/test/test.log b/framework/src/audit/auparse/test/test.log new file mode 100644 index 00000000..e0ffabf5 --- /dev/null +++ b/framework/src/audit/auparse/test/test.log @@ -0,0 +1,10 @@ +type=AVC msg=audit(1170021493.977:293): avc: denied { read write } for pid=13010 comm="pickup" name="maildrop" dev=hda7 ino=14911367 scontext=system_u:system_r:postfix_pickup_t:s0 tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 tclass=dir +type=SYSCALL msg=audit(1170021493.977:293): arch=c000003e syscall=2 success=no exit=-13 a0=5555665d91b0 a1=10800 a2=5555665d91b8 a3=0 items=1 ppid=2013 pid=13010 auid=4294967295 uid=890 gid=890 euid=890 suid=890 fsuid=890 egid=890 sgid=890 fsgid=890 tty=(none) comm="pickup" exe="/usr/libexec/postfix/pickup" subj=system_u:system_r:postfix_pickup_t:s0 key=(null) +type=CWD msg=audit(1170021493.977:293): cwd="/var/spool/postfix" +type=PATH msg=audit(1170021493.977:293): item=0 name="maildrop" inode=14911367 dev=03:07 mode=040730 ouid=890 ogid=891 rdev=00:00 obj=system_u:object_r:postfix_spool_maildrop_t:s0 +type=USER_ACCT msg=audit(1170021601.340:294): user pid=13015 uid=0 auid=4294967295 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: accounting acct=root : exe="/usr/sbin/crond" hostname=? addr=? terminal=cron res=success' +type=CRED_ACQ msg=audit(1170021601.342:295): user pid=13015 uid=0 auid=4294967295 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: setcred acct=root : exe="/usr/sbin/crond" hostname=? addr=? terminal=cron res=success' +type=LOGIN msg=audit(1170021601.343:296): login pid=13015 uid=0 old auid=4294967295 new auid=0 +type=USER_START msg=audit(1170021601.344:297): user pid=13015 uid=0 auid=0 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: session open acct=root : exe="/usr/sbin/crond" (hostname=?, addr=?, terminal=cron res=success)' +type=CRED_DISP msg=audit(1170021601.364:298): user pid=13015 uid=0 auid=0 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: setcred acct=root : exe="/usr/sbin/crond" (hostname=?, addr=?, terminal=cron res=success)' +type=USER_END msg=audit(1170021601.366:299): user pid=13015 uid=0 auid=0 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: session close acct=root : exe="/usr/sbin/crond" (hostname=?, addr=?, terminal=cron res=success)' diff --git a/framework/src/audit/auparse/test/test2.log b/framework/src/audit/auparse/test/test2.log new file mode 100644 index 00000000..588f1e04 --- /dev/null +++ b/framework/src/audit/auparse/test/test2.log @@ -0,0 +1,10 @@ +type=AVC msg=audit(1170021493.977:293): avc: denied { read } for pid=13010 comm="pickup" name="maildrop" dev=hda7 ino=14911367 scontext=system_u:system_r:postfix_pickup_t:s0 tcontext=system_u:object_r:postfix_spool_maildrop_t:s0 tclass=dir +type=SYSCALL msg=audit(1170021493.977:293): arch=c000003e syscall=2 success=no exit=-13 a0=5555665d91b0 a1=10800 a2=5555665d91b8 a3=0 items=1 ppid=2013 pid=13010 auid=4294967295 uid=890 gid=890 euid=890 suid=890 fsuid=890 egid=890 sgid=890 fsgid=890 tty=(none) comm="pickup" exe="/usr/libexec/postfix/pickup" subj=system_u:system_r:postfix_pickup_t:s0 key=(null) +type=CWD msg=audit(1170021493.977:293): cwd="/var/spool/postfix" +type=PATH msg=audit(1170021493.977:293): item=0 name="maildrop" inode=14911367 dev=03:07 mode=040730 ouid=890 ogid=891 rdev=00:00 obj=system_u:object_r:postfix_spool_maildrop_t:s0 +type=USER_ACCT msg=audit(1170021601.340:294): user pid=13015 uid=0 auid=4294967295 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: accounting acct=root : exe="/usr/sbin/crond" hostname=? addr=? terminal=cron res=success' +type=CRED_ACQ msg=audit(1170021601.342:295): user pid=13015 uid=0 auid=4294967295 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: setcred acct=root : exe="/usr/sbin/crond" hostname=? addr=? terminal=cron res=success' +type=LOGIN msg=audit(1170021601.343:296): login pid=13015 uid=0 old auid=4294967295 new auid=0 +type=USER_START msg=audit(1170021601.344:297): user pid=13015 uid=0 auid=0 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: session open acct=root : exe="/usr/sbin/crond" (hostname=?, addr=?, terminal=cron res=success)' +type=CRED_DISP msg=audit(1170021601.364:298): user pid=13015 uid=0 auid=0 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: setcred acct=root : exe="/usr/sbin/crond" (hostname=?, addr=?, terminal=cron res=success)' +type=USER_END msg=audit(1170021601.366:299): user pid=13015 uid=0 auid=0 subj=system_u:system_r:crond_t:s0-s0:c0.c1023 msg='PAM: session close acct=root : exe="/usr/sbin/crond" (hostname=?, addr=?, terminal=cron res=success)' diff --git a/framework/src/audit/auparse/tty_named_keys.h b/framework/src/audit/auparse/tty_named_keys.h new file mode 100644 index 00000000..e71ae11e --- /dev/null +++ b/framework/src/audit/auparse/tty_named_keys.h @@ -0,0 +1,409 @@ +/* tty_named_keys.h -- + * Copyright 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Miloslav Trmač + */ + +/* Longest sequences should go first, but these are comparatively common. */ +E("\x01", "^A") +E("\x02", "^B") +E("\x03", "^C") // Or "cancel" (3 terms) +E("\x04", "^D") +E("\x05", "^E") +E("\x06", "^F") +E("\x07", "^G") +E("\x08", "backspace") +E("\t", "tab") +E("\n", "nl") +E("\x0B", "^K") +E("\x0C", "^L") +E("\r", "ret") +E("\x0E", "^N") +E("\x0F", "^O") +E("\x10", "^P") +E("\x11", "^Q") +E("\x12", "^R") +E("\x13", "^S") +E("\x14", "^T") +E("\x15", "^U") +E("\x16", "^V") +E("\x17", "^W") +E("\x18", "^X") +E("\x19", "^Y") +E("\x1A", "^Z") // Or "suspend" (9 terms) +/* \x1B handled only after all other escape sequences */ +E("\x7F", "backspace") // 59 terms; alternative: "delete" (11 terms) + +// Based on terminal descriptions in ncrses-base-5.6-20.20080927.fc10. +// Conflicts are marked by comments. Ordering: longest sequences first, then +// lexicographically. +E("\x1B[11;2~", "F13") +E("\x1B[11;3~", "F49") +E("\x1B[11;4~", "F61") +E("\x1B[11;5~", "F25") +E("\x1B[11;6~", "F37") +E("\x1B[12;2~", "F14") +E("\x1B[12;3~", "F50") +E("\x1B[12;4~", "F62") +E("\x1B[12;5~", "F26") +E("\x1B[12;6~", "F38") +E("\x1B[13;2~", "F15") +E("\x1B[13;3~", "F51") +E("\x1B[13;4~", "F63") +E("\x1B[13;5~", "F27") +E("\x1B[13;6~", "F39") +E("\x1B[14;2~", "F16") +E("\x1B[14;3~", "F52") +E("\x1B[14;5~", "F28") +E("\x1B[14;6~", "F40") +E("\x1B[15;2~", "F17") +E("\x1B[15;3~", "F53") +E("\x1B[15;5~", "F29") +E("\x1B[15;6~", "F41") +E("\x1B[17;2~", "F18") +E("\x1B[17;3~", "F54") +E("\x1B[17;5~", "F30") +E("\x1B[17;6~", "F42") +E("\x1B[18;2~", "F19") +E("\x1B[18;3~", "F55") +E("\x1B[18;5~", "F31") +E("\x1B[18;6~", "F43") +E("\x1B[19;2~", "F20") +E("\x1B[19;3~", "F56") +E("\x1B[19;5~", "F32") +E("\x1B[19;6~", "F44") +E("\x1B[20;2~", "F21") +E("\x1B[20;3~", "F57") +E("\x1B[20;5~", "F33") +E("\x1B[20;6~", "F45") +E("\x1B[21;2~", "F22") +E("\x1B[21;3~", "F58") +E("\x1B[21;5~", "F34") +E("\x1B[21;6~", "F46") +E("\x1B[23;2~", "F23") +E("\x1B[23;3~", "F59") +E("\x1B[23;5~", "F35") +E("\x1B[23;6~", "F47") +E("\x1B[24;2~", "F24") +E("\x1B[24;3~", "F60") +E("\x1B[24;5~", "F36") +E("\x1B[24;6~", "F48") +E("\x1B""O1;2A", "scroll-backward") +E("\x1B""O1;2B", "scroll-forward") +E("\x1B""O1;2C", "shift-right") +E("\x1B""O1;2D", "shift-left") +E("\x1B[192z", "F11") +E("\x1B[193z", "resume") // 3 terms; alternative "F12" (1 term) +E("\x1B[194z", "options") // 3 terms; alternative "F13" (1 term) +E("\x1B[195z", "undo") // 4 terms; alternative "F14" (1 term) +E("\x1B[196z", "help") // 1 term; alternative "F15" (1 term) +E("\x1B[197z", "copy") +E("\x1B[198z", "F17") +E("\x1B[199z", "F18") +E("\x1B[1;2A", "scroll-backward") +E("\x1B[1;2B", "scroll-forward") +E("\x1B[1;2C", "shift-right") +E("\x1B[1;2D", "shift-left") +E("\x1B[1;2F", "shift-end") +E("\x1B[1;2H", "shift-home") +E("\x1B[200z", "find") // 1 term; alternative "F19" (1 term) +E("\x1B[201z", "F20") +E("\x1B[208z", "F31") +E("\x1B[209z", "F32") +E("\x1B[210z", "F33") +E("\x1B[211z", "F34") +E("\x1B[212z", "F35") +E("\x1B[213z", "F36") +E("\x1B[214z", "home") +E("\x1B[215z", "F38") +E("\x1B[216z", "page-up") +E("\x1B[217z", "F40") +E("\x1B[218z", "B2") +E("\x1B[219z", "F42") +E("\x1B[220z", "end") +E("\x1B[221z", "F44") +E("\x1B[222z", "page-down") // 4 terms; alternative "F45" (1 term) +E("\x1B[224z", "F1") +E("\x1B[225z", "F2") +E("\x1B[226z", "F3") +E("\x1B[227z", "F4") +E("\x1B[228z", "F5") +E("\x1B[229z", "F6") +E("\x1B[230z", "F7") +E("\x1B[231z", "F8") +E("\x1B[232z", "F9") +E("\x1B[233z", "F10") +E("\x1B[234z", "F11") // 3 terms; alternative "F46" (1 term) +E("\x1B[235z", "F12") // 3 terms; alternative "F47" (1 term) +E("\x1B[2;2~", "shift-insert") +E("\x1B[2;5~", "shift-insert") +E("\x1B[3;2~", "shift-del") +E("\x1B[3;5~", "shift-del") +E("\x1B[5;2~", "shift-previous") +E("\x1B[5;5~", "shift-previous") +E("\x1B[6;2~", "shift-next") +E("\x1B[6;5~", "shift-next") +E("\x1B[11^", "F23") +E("\x1B[11~", "F1") +E("\x1B[12^", "F24") +E("\x1B[12~", "F2") +E("\x1B[13^", "F25") +E("\x1B[13~", "F3") +E("\x1B[14^", "F26") +E("\x1B[14~", "F4") +E("\x1B[15^", "F27") +E("\x1B[15~", "F5") +E("\x1B[17^", "F28") +E("\x1B[17~", "F6") +E("\x1B[18^", "F29") +E("\x1B[18~", "F7") +E("\x1B[19^", "F30") +E("\x1B[19~", "F8") +E("\x1B[20^", "F31") +E("\x1B[20~", "F9") +E("\x1B[21^", "F32") +E("\x1B[21~", "F10") // 85 terms; alternative "F0" (9 terms) +E("\x1B[23$", "F21") +E("\x1B[23@", "F43") +E("\x1B[23^", "F33") +E("\x1B[23~", "F11") +E("\x1B[24$", "F22") +E("\x1B[24@", "F44") +E("\x1B[24^", "F34") +E("\x1B[24~", "F12") +E("\x1B[25^", "F35") +E("\x1B[25~", "F13") +E("\x1B[26^", "F36") +E("\x1B[26~", "F14") +E("\x1B[28^", "F37") +E("\x1B[28~", "F15") // 42 terms; alternative "help" (8 terms) +E("\x1B[29^", "F38") +E("\x1B[29~", "F16") // 42 terms; alternative "redo" (4 terms) +E("\x1B[30~", "insert-line") +E("\x1B[31^", "F39") +E("\x1B[31~", "F17") // 46 terms; alternative "delete-line" (1 term) +E("\x1B[32^", "F40") +E("\x1B[32~", "F18") +E("\x1B[33^", "F41") +E("\x1B[33~", "F19") +E("\x1B[34^", "F42") +E("\x1B[34~", "F20") +E("\x1B""O2A", "scroll-backward") +E("\x1B""O2B", "scroll-forward") +E("\x1B""O2C", "shift-right") +E("\x1B""O2D", "shift-left") +E("\x1B""O2P", "F13") +E("\x1B""O2Q", "F14") +E("\x1B""O2R", "F15") +E("\x1B""O2S", "F16") +E("\x1B""O3P", "F49") +E("\x1B""O3Q", "F50") +E("\x1B""O3R", "F51") +E("\x1B""O3S", "F52") +E("\x1B""O4P", "F61") +E("\x1B""O4Q", "F62") +E("\x1B""O4R", "F63") +E("\x1B""O5C", "shift-right") +E("\x1B""O5D", "shift-left") +E("\x1B""O5F", "shift-end") +E("\x1B""O5H", "shift-home") +E("\x1B""O5P", "F25") +E("\x1B""O5Q", "F26") +E("\x1B""O5R", "F27") +E("\x1B""O5S", "F28") +E("\x1B""O6P", "F37") +E("\x1B""O6Q", "F38") +E("\x1B""O6R", "F39") +E("\x1B""O6S", "F40") +E("\x1B[1~", "home") // 30 terms; alternative "find" (42 terms, but "home" is used in Linux) +E("\x1B[2$", "shift-insert") +E("\x1B[2z", "insert") +E("\x1B[2~", "insert") +E("\x1B[3$", "shift-del") +E("\x1B[3z", "delete") +E("\x1B[3~", "delete") +E("\x1B[4~", "end") // 30 terms; alternative "select" (42 terms, but "end" is used in Linux) +E("\x1B[5$", "shift-previous") +E("\x1B[5~", "page-up") // 86 terms; alternative "A3" (4 terms) +E("\x1B[6$", "shift-next") +E("\x1B[6~", "page-down") // 86 terms; alternative "C3" (4 terms) +E("\x1B[7$", "shift-home") +E("\x1B[7~", "home") // 17 terms; alternative "A1" (4 terms) +E("\x1B[8$", "shift-end") +E("\x1B[8^", "delete-eol") +E("\x1B[8~", "end") // 17 terms; alternatives "C1" (4 terms), "delete-eol" (1 term) +E("\x1B[>M", "mouse") +E("\x1B[[A", "F1") +E("\x1B[[B", "F2") +E("\x1B[[C", "F3") +E("\x1B[[D", "F4") +E("\x1B[[E", "F5") +E("\x9B""11~", "F1") +E("\x9B""12~", "F2") +E("\x9B""13~", "F3") +E("\x9B""14~", "F4") +E("\x9B""15~", "F5") +E("\x9B""17~", "F6") +E("\x9B""18~", "F7") +E("\x9B""19~", "F8") +E("\x9B""20~", "F9") +E("\x9B""21~", "F10") +E("\x9B""23~", "F11") +E("\x9B""24~", "F12") +E("\x9B""25~", "F13") +E("\x9B""26~", "F14") +E("\x9B""28~", "F15") +E("\x9B""29~", "F16") +E("\x9B""31~", "F17") +E("\x9B""32~", "F18") +E("\x9B""33~", "F19") +E("\x9B""34~", "F20") +E("\x1B""2$", "shift-insert") +E("\x1B""OA", "up") +E("\x1B""OB", "down") +E("\x1B""OC", "right") +E("\x1B""OD", "left") +E("\x1B""OE", "B2") // 16 terms; alternative "begin" (5 terms) +E("\x1B""OF", "end") +E("\x1B""OH", "home") +E("\x1B""OM", "send") +E("\x1B""OP", "F1") +E("\x1B""OQ", "F2") +E("\x1B""OR", "F3") +E("\x1B""OS", "F4") +E("\x1B""OT", "F5") +E("\x1B""OU", "F6") +E("\x1B""OV", "F7") +E("\x1B""OW", "F8") +E("\x1B""OX", "F9") +E("\x1B""OY", "F10") +E("\x1B""OZ", "F11") +E("\x1B""O[", "F12") +E("\x1B""Ol", "F8") +E("\x1B""On", "C3") +E("\x1B""Op", "C1") +E("\x1B""Oq", "C1") // 17 terms; alternatives "A1" (5 terms), "F0" (1 term) +E("\x1B""Or", "B2") +E("\x1B""Os", "C3") // 17 terms; alternative "A3" (7 terms) +E("\x1B""Ot", "F5") +E("\x1B""Ou", "B2") // 21 terms; alternative "F6" (4 terms), "begin" (4 terms) +E("\x1B""Ov", "F7") +E("\x1B""Ow", "A1") // 17 terms; alternative "F9" (4 terms) +E("\x1B""Ox", "F10") +E("\x1B""Oy", "A3") // 17 terms; alternative "F0" (5 terms) +E("\x1B[9", "delete") +E("\x1B[@", "F41") // 4 terms; alternative "insert" (3 terms) +E("\x1B[A", "up") +E("\x1B[B", "down") +E("\x1B[C", "right") +E("\x1B[D", "left") +E("\x1B[E", "B2") // 9 terms; alternative "begin" (1 term) +E("\x1B[F", "end") // 5 terms; alternative "lower-left" (3 terms) +E("\x1B[G", "B2") // 9 terms; alternative "page-down" (4 terms) +E("\x1B[H", "home") +E("\x1B[I", "page-up") +E("\x1B[L", "insert") +E("\x1B[M", "mouse") // 83 terms; alternative "F1" (4 terms) +E("\x1B[N", "F2") +E("\x1B[O", "F3") +E("\x1B[P", "F4") +E("\x1B[Q", "F5") +E("\x1B[R", "F6") +E("\x1B[S", "F7") +E("\x1B[T", "F8") +E("\x1B[U", "F9") // 4 terms; alternative "page-down" (3 terms) +E("\x1B[V", "F10") // 4 terms; alternative "page-dup" (3 terms) +E("\x1B[W", "F11") +E("\x1B[X", "F12") +E("\x1B[Y", "F13") // 4 terms; alternative "end" (3 terms) +E("\x1B[Z", "back-tab") // 59 terms; alternative "F14" (4 terms) +E("\x1B[[", "F42") +E("\x1B[\\", "F43") +E("\x1B[]", "F44") +E("\x1B[^", "F45") +E("\x1B[_", "F46") +E("\x1B[`", "F47") +E("\x1B[a", "F15") +E("\x1B[b", "F16") +E("\x1B[c", "shift-right") // 15 terms; alternative "F17" (4 terms) +E("\x1B[d", "shift-left") // 15 terms; alternative "F18" (4 terms) +E("\x1B[e", "F19") +E("\x1B[f", "F20") +E("\x1B[g", "F21") +E("\x1B[h", "F22") +E("\x1B[i", "F23") +E("\x1B[j", "F24") +E("\x1B[k", "F25") +E("\x1B[l", "F26") +E("\x1B[m", "F27") +E("\x1B[n", "F28") +E("\x1B[o", "F29") +E("\x1B[p", "F30") +E("\x1B[q", "F31") +E("\x1B[r", "F32") +E("\x1B[s", "F33") +E("\x1B[t", "F34") +E("\x1B[u", "F35") +E("\x1B[v", "F36") +E("\x1B[w", "F37") +E("\x1B[x", "F38") +E("\x1B[y", "F39") +E("\x1B[z", "F40") +E("\x1B[{", "F48") +E("\x9B""1~", "home") +E("\x9B""2~", "insert") +E("\x9B""3~", "delete") +E("\x9B""4~", "end") +E("\x9B""5~", "page-up") +E("\x9B""6~", "page-down") +E("\x1B""A", "up") +E("\x1B""B", "down") +E("\x1B""C", "right") +E("\x1B""D", "left") +E("\x1B""F", "end") +E("\x1B""J", "clear") +E("\x1B""P", "delete") +E("\x1B""Q", "insert") +E("\x1B""S", "page-down") +E("\x1B""T", "page-up") +E("\x1B""h", "home") +E("\x1B""p", "F1") +E("\x1B""q", "F2") +E("\x1B""r", "F3") +E("\x1B""s", "F4") +E("\x1B""t", "F5") +E("\x1B""u", "F6") +E("\x1B""v", "F7") +E("\x1B""w", "F8") +E("\x1B\x09", "back-tab") +E("\x8F""A", "up") +E("\x8F""B", "down") +E("\x8F""C", "right") +E("\x8F""D", "left") +E("\x8F""E", "begin") +E("\x8F""M", "send") +E("\x8F""q", "C1") +E("\x8F""s", "C3") +E("\x8F""u", "A3") +E("\x8F""w", "A1") +E("\x8F""y", "B2") +E("\x9B""M", "mouse") +E("\x9B""Z", "back-tab") + +E("\x1B", "esc") diff --git a/framework/src/audit/auparse/typetab.h b/framework/src/audit/auparse/typetab.h new file mode 100644 index 00000000..7ff53c31 --- /dev/null +++ b/framework/src/audit/auparse/typetab.h @@ -0,0 +1,127 @@ +/* typetab.h -- + * Copyright 2007-09,2011-12,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + + +_S(AUPARSE_TYPE_UID, "auid" ) +_S(AUPARSE_TYPE_UID, "uid" ) +_S(AUPARSE_TYPE_UID, "euid" ) +_S(AUPARSE_TYPE_UID, "suid" ) +_S(AUPARSE_TYPE_UID, "fsuid" ) +_S(AUPARSE_TYPE_UID, "ouid" ) +_S(AUPARSE_TYPE_UID, "oauid" ) +_S(AUPARSE_TYPE_UID, "iuid" ) +_S(AUPARSE_TYPE_UID, "id" ) +_S(AUPARSE_TYPE_UID, "inode_uid" ) +_S(AUPARSE_TYPE_UID, "sauid" ) +_S(AUPARSE_TYPE_UID, "obj_uid" ) +_S(AUPARSE_TYPE_GID, "obj_gid" ) +_S(AUPARSE_TYPE_GID, "gid" ) +_S(AUPARSE_TYPE_GID, "egid" ) +_S(AUPARSE_TYPE_GID, "sgid" ) +_S(AUPARSE_TYPE_GID, "fsgid" ) +_S(AUPARSE_TYPE_GID, "ogid" ) +_S(AUPARSE_TYPE_GID, "igid" ) +_S(AUPARSE_TYPE_GID, "inode_gid" ) +_S(AUPARSE_TYPE_GID, "new_gid" ) +_S(AUPARSE_TYPE_SYSCALL, "syscall" ) +_S(AUPARSE_TYPE_ARCH, "arch" ) +_S(AUPARSE_TYPE_EXIT, "exit" ) +_S(AUPARSE_TYPE_ESCAPED, "path" ) +_S(AUPARSE_TYPE_ESCAPED, "comm" ) +_S(AUPARSE_TYPE_ESCAPED, "exe" ) +_S(AUPARSE_TYPE_ESCAPED, "file" ) +_S(AUPARSE_TYPE_ESCAPED, "name" ) +_S(AUPARSE_TYPE_ESCAPED, "watch" ) +_S(AUPARSE_TYPE_ESCAPED, "cwd" ) +_S(AUPARSE_TYPE_ESCAPED, "cmd" ) +_S(AUPARSE_TYPE_ESCAPED, "acct" ) +_S(AUPARSE_TYPE_ESCAPED, "dir" ) +_S(AUPARSE_TYPE_ESCAPED, "key" ) +_S(AUPARSE_TYPE_ESCAPED, "vm" ) +_S(AUPARSE_TYPE_ESCAPED, "old-disk" ) +_S(AUPARSE_TYPE_ESCAPED, "new-disk" ) +_S(AUPARSE_TYPE_ESCAPED, "old-fs" ) +_S(AUPARSE_TYPE_ESCAPED, "new-fs" ) +_S(AUPARSE_TYPE_ESCAPED, "device" ) +_S(AUPARSE_TYPE_ESCAPED, "cgroup" ) +_S(AUPARSE_TYPE_PERM, "perm" ) +_S(AUPARSE_TYPE_PERM, "perm_mask" ) +_S(AUPARSE_TYPE_MODE, "mode" ) +_S(AUPARSE_TYPE_SOCKADDR, "saddr" ) +//_S(AUPARSE_TYPE_FLAGS, "flags" ) +_S(AUPARSE_TYPE_PROMISC, "prom" ) +_S(AUPARSE_TYPE_PROMISC, "old_prom" ) +_S(AUPARSE_TYPE_CAPABILITY, "capability" ) +_S(AUPARSE_TYPE_SUCCESS, "res" ) +_S(AUPARSE_TYPE_SUCCESS, "result" ) +_S(AUPARSE_TYPE_A0, "a0" ) +_S(AUPARSE_TYPE_A1, "a1" ) +_S(AUPARSE_TYPE_A2, "a2" ) +_S(AUPARSE_TYPE_A3, "a3" ) +_S(AUPARSE_TYPE_SIGNAL, "sig" ) +_S(AUPARSE_TYPE_LIST, "list" ) +_S(AUPARSE_TYPE_TTY_DATA, "data" ) +_S(AUPARSE_TYPE_SESSION, "ses" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "cap_pi" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "cap_pe" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "cap_pp" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "cap_fi" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "cap_fp" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "fp" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "fi" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "fe" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "old_pp" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "old_pi" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "old_pe" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "new_pp" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "new_pi" ) +_S(AUPARSE_TYPE_CAP_BITMAP, "new_pe" ) +_S(AUPARSE_TYPE_NFPROTO, "family" ) +_S(AUPARSE_TYPE_ICMPTYPE, "icmptype" ) +_S(AUPARSE_TYPE_PROTOCOL, "proto" ) +_S(AUPARSE_TYPE_ADDR, "addr" ) +#ifdef WITH_APPARMOR +_S(AUPARSE_TYPE_ESCAPED, "apparmor" ) +_S(AUPARSE_TYPE_ESCAPED, "operation" ) +_S(AUPARSE_TYPE_ESCAPED, "denied_mask" ) +_S(AUPARSE_TYPE_ESCAPED, "info" ) +_S(AUPARSE_TYPE_ESCAPED, "profile" ) +_S(AUPARSE_TYPE_ESCAPED, "requested_mask") +#endif +_S(AUPARSE_TYPE_PERSONALITY, "per" ) +_S(AUPARSE_TYPE_SECCOMP, "code" ) +_S(AUPARSE_TYPE_ESCAPED, "old-rng" ) +_S(AUPARSE_TYPE_ESCAPED, "new-rng" ) +_S(AUPARSE_TYPE_OFLAG, "oflag" ) +_S(AUPARSE_TYPE_ESCAPED, "ocomm" ) +_S(AUPARSE_TYPE_MMAP, "flags" ) +_S(AUPARSE_TYPE_SIGNAL, "sigev_signo" ) +_S(AUPARSE_TYPE_MAC_LABEL, "subj" ) +_S(AUPARSE_TYPE_MAC_LABEL, "obj" ) +_S(AUPARSE_TYPE_MAC_LABEL, "scontext" ) +_S(AUPARSE_TYPE_MAC_LABEL, "tcontext" ) +_S(AUPARSE_TYPE_MAC_LABEL, "vm-ctx" ) +_S(AUPARSE_TYPE_MAC_LABEL, "img-ctx" ) +_S(AUPARSE_TYPE_PROCTITLE, "proctitle" ) +_S(AUPARSE_TYPE_ESCAPED, "grp" ) +_S(AUPARSE_TYPE_ESCAPED, "new_group" ) + diff --git a/framework/src/audit/auparse/umounttab.h b/framework/src/audit/auparse/umounttab.h new file mode 100644 index 00000000..a673efb1 --- /dev/null +++ b/framework/src/audit/auparse/umounttab.h @@ -0,0 +1,30 @@ +/* umounttab.h -- + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Location: include/linux/fs.h + */ + + +_S(0x00000001, "MNT_FORCE" ) +_S(0x00000002, "MNT_DETACH" ) +_S(0x00000004, "MNT_EXPIRE" ) +_S(0x00000008, "UMOUNT_NOFOLLOW" ) +_S(0x80000001, "UMOUNT_UNUSED" ) + diff --git a/framework/src/audit/autogen.sh b/framework/src/audit/autogen.sh new file mode 100755 index 00000000..205fd26b --- /dev/null +++ b/framework/src/audit/autogen.sh @@ -0,0 +1,5 @@ +#! /bin/sh +set -x -e +# --no-recursive is available only in recent autoconf versions +autoreconf -fv --install +mv INSTALL.tmp INSTALL diff --git a/framework/src/audit/bindings/Makefile.am b/framework/src/audit/bindings/Makefile.am new file mode 100644 index 00000000..cc68df34 --- /dev/null +++ b/framework/src/audit/bindings/Makefile.am @@ -0,0 +1,25 @@ +# Makefile.am -- +# Copyright 2007,2014,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig + +SUBDIRS = @pybind_dir@ @gobind_dir@ swig diff --git a/framework/src/audit/bindings/golang/Makefile.am b/framework/src/audit/bindings/golang/Makefile.am new file mode 100644 index 00000000..4332b8cb --- /dev/null +++ b/framework/src/audit/bindings/golang/Makefile.am @@ -0,0 +1,45 @@ +# Makefile.am -- +# Copyright 2014 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +EXTRA_DIST = audit.go + +LIBDIR = lib +GODIR = $(LIBDIR)/golang/src/pkg/redhat.com/audit +dist_check_SCRIPTS = test.go + +install: + [ -d $(DESTDIR)${prefix}/$(GODIR) ] || mkdir -p $(DESTDIR)${prefix}/$(GODIR) + install -m 644 ${top_srcdir}/bindings/golang/audit.go $(DESTDIR)${prefix}/$(GODIR) + +uninstall: + @rm -f $(DESTDIR)${prefix}/$(GODIR)/* + +check: + @mkdir audit + @cp ${top_srcdir}/bindings/golang/audit.go audit + @cp ${top_srcdir}/lib/libaudit.h audit + ## Disable for now. Golang doesn't allow overriding search + ## paths from the command line. + ##[ -f test.go ] || cp ${top_srcdir}/bindings/golang/test.go . + ##PKG_CONFIG_PATH=${abs_top_builddir}/lib/:$(PKG_CONFIG_PATH) GOPATH=$(pwd) $(GOLANG) run test.go + @rm -rf audit diff --git a/framework/src/audit/bindings/golang/audit.go b/framework/src/audit/bindings/golang/audit.go new file mode 100644 index 00000000..d060ddcb --- /dev/null +++ b/framework/src/audit/bindings/golang/audit.go @@ -0,0 +1,72 @@ +package audit + +/* + The audit package is a go bindings to libaudit that only allows for + logging audit events. + + Author Steve Grubb + +*/ + +// #cgo pkg-config: audit +// #include "libaudit.h" +// #include +// #include +// #include +// #include +import "C" + +import ( + "unsafe" +) + +const ( + AUDIT_VIRT_CONTROL = 2500 + AUDIT_VIRT_RESOURCE = 2501 + AUDIT_VIRT_MACHINE_ID = 2502 +) + +// type=VIRT_CONTROL msg=audit(08/05/2014 17:01:05.891:6471) : pid=1265 uid=root auid=unset ses=unset subj=system_u:system_r:virtd_t:s0-s0:c0.c1023 msg='virt=kvm op=start reason=booted vm=vm1 uuid=462dcd6d-fb68-4a26-a96f-56eb024515b9 vm-pid=22527 exe=/usr/sbin/libvirtd hostname=? addr=? terminal=? res=success' + +func AuditValueNeedsEncoding(str string) bool { + cstr := C.CString(str) + defer C.free(unsafe.Pointer(cstr)) + len := C.strlen(cstr) + + res, _ := C.audit_value_needs_encoding(cstr, C.uint(len)) + if res != 0 { + return true + } + return false +} + +func AuditEncodeNVString(name string, value string) string { + cname := C.CString(name) + cval := C.CString(value) + + cres := C.audit_encode_nv_string(cname, cval, 0) + + C.free(unsafe.Pointer(cname)) + C.free(unsafe.Pointer(cval)) + defer C.free(unsafe.Pointer(cres)) + + return C.GoString(cres) +} + +func AuditLogUserEvent(event_type int, message string, result bool) error { + var r int + fd := C.audit_open() + if result { + r = 1 + } else { + r = 0 + } + if fd > 0 { + cmsg := C.CString(message) + _, err := C.audit_log_user_message(fd, C.int(event_type), cmsg, nil, nil, nil, C.int(r)) + C.free(unsafe.Pointer(cmsg)) + C.close(fd) + return err + } + return nil +} diff --git a/framework/src/audit/bindings/golang/test.go b/framework/src/audit/bindings/golang/test.go new file mode 100644 index 00000000..7d9ab7ee --- /dev/null +++ b/framework/src/audit/bindings/golang/test.go @@ -0,0 +1,18 @@ +package main + +import ( + "./audit" + "fmt" +) + +func main() { + if audit.AuditValueNeedsEncoding("test") { + fmt.Printf("Failed test 1\n") + return + } + if !audit.AuditValueNeedsEncoding("test test") { + fmt.Printf("Failed test 2\n") + return + } + fmt.Printf("Success\n") +} diff --git a/framework/src/audit/bindings/python/Makefile.am b/framework/src/audit/bindings/python/Makefile.am new file mode 100644 index 00000000..1f91fa99 --- /dev/null +++ b/framework/src/audit/bindings/python/Makefile.am @@ -0,0 +1,11 @@ +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +EXTRA_DIST = auparse_python.c + +SUBDIRS = +if HAVE_PYTHON +SUBDIRS += python2 +endif +if USE_PYTHON3 +SUBDIRS += python3 +endif + diff --git a/framework/src/audit/bindings/python/auparse_python.c b/framework/src/audit/bindings/python/auparse_python.c new file mode 100644 index 00000000..af19194c --- /dev/null +++ b/framework/src/audit/bindings/python/auparse_python.c @@ -0,0 +1,1776 @@ +#include +#include "structmember.h" + +#include +#include +#include "auparse.h" + +/* +auparse functions explicitly not exported in this binding and why: + +auparse_destroy: because this is handled by python object management +auparse_get_time: because AuEvent provides this as an attribute +auparse_get_milli: because AuEvent provides this as an attribute +auparse_get_serial: because AuEvent provides this as an attribute +auparse_get_node: because AuEvent provides this as an attribute +auparse_timestamp_compare: because AuEvent calls this via the cmp operator + +*/ + +#if PY_MAJOR_VERSION > 2 +#define IS_PY3K +#define MODINITERROR return NULL +#define PYNUM_FROMLONG PyLong_FromLong +#define PYSTR_CHECK PyUnicode_Check +#define PYSTR_FROMSTRING PyUnicode_FromString +#define PYSTR_ASSTRING PyUnicode_AsUTF8 +#define PYFILE_ASFILE(f) fdopen(PyObject_AsFileDescriptor(f), "r") +int PyFile_Check(PyObject *f) { + PyObject *io, *base; + if (!(io = PyImport_ImportModule("io"))) { + return 0; + } else { + if (!(base = PyObject_GetAttrString(io, "TextIOBase"))) { + return 0; + } else { + return PyObject_IsInstance(f, base); + } + } +} +#else +#define MODINITERROR return +#define PYNUM_FROMLONG PyInt_FromLong +#define PYSTR_CHECK PyString_Check +#define PYSTR_FROMSTRING PyString_FromString +#define PYSTR_ASSTRING PyString_AsString +#define PYFILE_ASFILE(f) PyFile_AsFile(f) +#endif + +static int debug = 0; +static PyObject *NoParserError = NULL; + +/*=========================================================================== + * AuEvent + *===========================================================================*/ + +typedef struct { + PyObject_HEAD + PyObject *sec; + PyObject *milli; + PyObject *serial; + PyObject *host; + au_event_t event; +} AuEvent; + +static void +AuEvent_dealloc(AuEvent* self) +{ + Py_XDECREF(self->sec); + Py_XDECREF(self->milli); + Py_XDECREF(self->serial); + Py_XDECREF(self->host); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static int +AuEvent_compare(PyObject *obj1, PyObject *obj2) +{ + AuEvent *au_event1 = (AuEvent *) obj1; + AuEvent *au_event2 = (AuEvent *) obj2; + + return auparse_timestamp_compare(&au_event1->event, &au_event2->event); +} + +static PyObject * +AuEvent_get_sec(AuEvent *self, void *closure) +{ + if (self->sec == NULL) { + if ((self->sec = PYNUM_FROMLONG(self->event.sec)) == NULL) return NULL; + } + Py_INCREF(self->sec); + return self->sec; +} + +static PyObject * +AuEvent_get_milli(AuEvent *self, void *closure) +{ + if (self->milli == NULL) { + if ((self->milli = PYNUM_FROMLONG(self->event.milli)) == NULL) return NULL; + } + Py_INCREF(self->milli); + return self->milli; +} + +static PyObject * +AuEvent_get_serial(AuEvent *self, void *closure) +{ + if (self->serial == NULL) { + if ((self->serial = PYNUM_FROMLONG(self->event.serial)) == NULL) return NULL; + } + Py_INCREF(self->serial); + return self->serial; +} + +static PyObject * +AuEvent_get_host(AuEvent *self, void *closure) +{ + if (self->event.host == NULL) { + Py_RETURN_NONE; + } else { + if (self->host == NULL) { + if ((self->host = PYSTR_FROMSTRING(self->event.host)) == NULL) return NULL; + } + Py_INCREF(self->host); + return self->host; + } +} + +static PyGetSetDef AuEvent_getseters[] = { + {"sec", (getter)AuEvent_get_sec, (setter)NULL, "Event seconds", NULL}, + {"milli", (getter)AuEvent_get_milli, (setter)NULL, "millisecond of the timestamp", NULL}, + {"serial", (getter)AuEvent_get_serial, (setter)NULL, "Serial number of the event", NULL}, + {"host", (getter)AuEvent_get_host, (setter)NULL, "Machine's name", NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef AuEvent_members[] = { + {NULL} /* Sentinel */ +}; + +static char * +fmt_event(time_t seconds, unsigned int milli, unsigned long serial, const char *host) +{ + static char buf1[200], buf2[200]; + char fmt[] = "%a %b %d %H:%M:%S.%%ld %Y serial=%%ld host=%%s"; + struct tm *tmp; + + tmp = localtime(&seconds); + if (tmp == NULL) { + sprintf(buf2, "localtime error"); + return buf2; + } + + if (strftime(buf1, sizeof(buf1), fmt, tmp) == 0) { + sprintf(buf2, "strftime returned 0"); + return buf2; + } + + snprintf(buf2, sizeof(buf2), buf1, milli, serial, host, sizeof(buf2)); + return buf2; +} + +static PyObject * +AuEvent_str(PyObject * obj) +{ + AuEvent *event = (AuEvent *) obj; + return PYSTR_FROMSTRING(fmt_event(event->event.sec, event->event.milli, event->event.serial, event->event.host)); +} + + +static PyMethodDef AuEvent_methods[] = { + {NULL} /* Sentinel */ +}; + +PyDoc_STRVAR(AuEvent_doc, +"An internal object which encapsulates the timestamp, serial number\n\ +and host information of an audit event. The object cannot be\n\ +instantiated from python code, rather it is returned from the\n\ +audit parsing API."); + +static PyTypeObject AuEventType = { + PyVarObject_HEAD_INIT(NULL, 0) + "auparse.AuEvent", /*tp_name*/ + sizeof(AuEvent), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)AuEvent_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + AuEvent_compare, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + AuEvent_str, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + AuEvent_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + AuEvent_methods, /* tp_methods */ + AuEvent_members, /* tp_members */ + AuEvent_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +static PyObject * +AuEvent_new_from_struct(au_event_t const *event_ptr) +{ + AuEvent *self; + + self = (AuEvent *)AuEventType.tp_alloc(&AuEventType, 0); + if (self != NULL) { + self->event = *event_ptr; + } + + return (PyObject *)self; +} + +/*=========================================================================== + * AuParser + *===========================================================================*/ + +#define PARSER_CHECK \ + if (self->au == NULL) { \ + PyErr_SetString(NoParserError, "object has no parser associated with it"); \ + return NULL; \ + } + +typedef struct { + PyObject_HEAD + auparse_state_t *au; +} AuParser; + +typedef struct { + AuParser *py_AuParser; + PyObject *func; + PyObject *user_data; +} CallbackData; + +void callback_data_destroy(void *user_data) +{ + CallbackData *cb = (CallbackData *)user_data; + + if (debug) printf("<< callback_data_destroy\n"); + if (cb) { + Py_DECREF(cb->func); + Py_XDECREF(cb->user_data); + PyMem_Del(cb); + } +} + +static void auparse_callback(auparse_state_t *au, auparse_cb_event_t cb_event_type, void *user_data) +{ + CallbackData *cb = (CallbackData *)user_data; + PyObject *arglist; + PyObject *result; + + arglist = Py_BuildValue("OiO", cb->py_AuParser, cb_event_type, cb->user_data); + result = PyEval_CallObject(cb->func, arglist); + Py_DECREF(arglist); + Py_XDECREF(result); +} + +static void +AuParser_dealloc(AuParser* self) +{ + if (debug) printf("<< AuParser_dealloc: self=%p au=%p\n", self, self->au); + if (self->au != NULL) { + auparse_destroy(self->au); + } + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +AuParser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + AuParser *self; + + self = (AuParser *)type->tp_alloc(type, 0); + if (self != NULL) { + self->au = NULL; + } + return (PyObject *)self; +} + +/******************************** + * auparse_init + ********************************/ +static int +AuParser_init(AuParser *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"source_type", "source", NULL}; + int source_type = -1; + PyObject *source=Py_None; + + if (self->au != NULL) { + auparse_destroy(self->au); + self->au = NULL; + } + + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|iO", kwlist, &source_type, &source)) return -1; + + switch (source_type) { + case AUSOURCE_LOGS: { + if (source != Py_None) { + PyErr_SetString(PyExc_ValueError, "source must be None or not passed as a parameter when source_type is AUSOURCE_LOGS"); + return -1; + } + if ((self->au = auparse_init(source_type, NULL)) == NULL) { + PyErr_SetFromErrno(PyExc_IOError); + return -1; + } + } break; + case AUSOURCE_FILE: { + char *filename = NULL; + + if (!PYSTR_CHECK(source)) { + PyErr_SetString(PyExc_ValueError, "source must be a string when source_type is AUSOURCE_FILE"); + return -1; + } + if ((filename = PYSTR_ASSTRING(source)) == NULL) return -1; + if ((self->au = auparse_init(source_type, filename)) == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); + return -1; + } + } break; + case AUSOURCE_FILE_ARRAY: { + int i, n; + PyObject *item = NULL; + char **files = NULL; + + if (PySequence_Check(source)) { + n = PySequence_Size(source); + if ((files = PyMem_New(char *, n+1)) == NULL) { + PyErr_NoMemory(); + return -1; + } + for (i = 0; i < n; i++) { + item = PySequence_GetItem(source, i); + if ((files[i] = PYSTR_ASSTRING(item)) == NULL) { + PyErr_SetString(PyExc_ValueError, "members of source sequence must be a string when source_type is AUSOURCE_FILE_ARRAY"); + Py_DECREF(item); + PyMem_Del(files); + return -1; + } else { + Py_DECREF(item); + } + } + files[i] = NULL; + } else { + PyErr_SetString(PyExc_ValueError, "source must be a sequence when source_type is AUSOURCE_FILE_ARRAY"); + return -1; + } + + if ((self->au = auparse_init(source_type, files)) == NULL) { + PyErr_SetFromErrno(PyExc_IOError); + PyMem_Del(files); + return -1; + } + PyMem_Del(files); + } break; + case AUSOURCE_BUFFER: { + char *buf; + if ((buf = PYSTR_ASSTRING(source)) == NULL) return -1; + if ((self->au = auparse_init(source_type, buf)) == NULL) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return -1; + } + } break; + case AUSOURCE_BUFFER_ARRAY: { + int i, n; + PyObject *item = NULL; + char **buffers = NULL; + + if (PySequence_Check(source)) { + n = PySequence_Size(source); + if ((buffers = PyMem_New(char *, n+1)) == NULL) { + PyErr_NoMemory(); + return -1; + } + for (i = 0; i < n; i++) { + item = PySequence_GetItem(source, i); + if ((buffers[i] = PYSTR_ASSTRING(item)) == NULL) { + PyErr_SetString(PyExc_ValueError, "members of source sequence must be a string when source_type is AUSOURCE_BUFFER_ARRAY"); + Py_DECREF(item); + PyMem_Del(buffers); + return -1; + } else { + Py_DECREF(item); + } + } + buffers[i] = NULL; + } else { + PyErr_SetString(PyExc_ValueError, "source must be a sequence when source_type is AUSOURCE_FILE_ARRAY"); + return -1; + } + + if ((self->au = auparse_init(source_type, buffers)) == NULL) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + PyMem_Del(buffers); + return -1; + } + PyMem_Del(buffers); + } break; + case AUSOURCE_DESCRIPTOR: { + int fd; + fd = PyObject_AsFileDescriptor(source); + if (fd < 0) { + PyErr_SetString(PyExc_ValueError, "source must be resolvable to a file descriptor when source_type is AUSOURCE_DESCRIPTOR"); + return -1; + } + } break; + case AUSOURCE_FILE_POINTER: { + FILE* fp; + + if (!PyFile_Check(source)) { + PyErr_SetString(PyExc_ValueError, "source must be a file object when source_type is AUSOURCE_FILE_POINTER"); + return -1; + } + if ((fp = PYFILE_ASFILE(source)) == NULL) { + PyErr_SetString(PyExc_TypeError, "source must be open file when source_type is AUSOURCE_FILE_POINTER"); + return -1; + } + if ((self->au = auparse_init(source_type, fp)) == NULL) { + //char *filename = PYSTR_ASSTRING(PyFile_Name(source)); + char *filename = "TODO"; + PyErr_SetFromErrnoWithFilename(PyExc_IOError, filename); + return -1; + } + } break; + case AUSOURCE_FEED: { + if (source != Py_None) { + PyErr_SetString(PyExc_ValueError, "source must be None when source_type is AUSOURCE_FEED"); + return -1; + } + if ((self->au = auparse_init(source_type, NULL)) == NULL) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return -1; + } + } break; + default: { + PyErr_SetString(PyExc_ValueError, "Invalid source type"); + return -1; + } break; + } + + if (debug) printf(">> AuParser_init: self=%p au=%p\n", self, self->au); + return 0; +} + +/******************************** + * auparse_feed + ********************************/ +PyDoc_STRVAR(feed_doc, +"feed(data) supplies new data for the parser to consume.\n\ +\n\ +AuParser() must have been called with a source type of AUSOURCE_FEED.\n\ +The parser consumes as much data as it can invoking a user supplied\n\ +callback specified with add_callback() with a cb_event_type of\n\ +AUPARSE_CB_EVENT_READY each time the parser recognizes a complete event\n\ +in the data stream. Data not fully parsed will persist and be prepended\n\ +to the next feed data. After all data has been feed to the parser flush_feed()\n\ +should be called to signal the end of input data and flush any pending\n\ +parse data through the parsing system.\n\ +\n\ +Returns None.\n\ +Raises exception (EnvironmentError) on error\n\ +"); +static PyObject * +AuParser_feed(AuParser *self, PyObject *args) +{ + char *data; + int data_len; + int result; + + if (!PyArg_ParseTuple(args, "s#:feed", &data, &data_len)) return NULL; + PARSER_CHECK; + result = auparse_feed(self->au, data, data_len); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_flush_feed + ********************************/ +PyDoc_STRVAR(flush_feed_doc, +"flush_feed() flush any unconsumed feed data through parser\n\ +\n\ +flush_feed() should be called to signal the end of feed input data\n\ +and flush any pending parse data through the parsing system.\n\ +\n\ +Returns None.\n\ +Raises exception (EnvironmentError) on error\n\ +"); +static PyObject * +AuParser_flush_feed(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_flush_feed(self->au); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_add_callback + ********************************/ +PyDoc_STRVAR(add_callback_doc, +"add_callback(callback, user_data) add a callback handler for notifications.\n\ +\n\ +auparse_add_callback adds a callback function to the parse state which\n\ +is invoked to notify the application of parsing events.\n\ +\n\ +The signature of the callback is:\n\ +\n\ +callback(au, cb_event_type,user_data)\n\ +\n\ +When the callback is invoked it is passed:\n\ +au: the AuParser object\n\ +cb_event_type: enumerated value indicating the reason why the callback was invoked\n\ +user_data: user supplied private data\n\ +\n\ +The cb_event_type argument indicates why the callback was invoked.\n\ +It's possible values are:\n\ +\n\ +AUPARSE_CB_EVENT_READY\n\ +A complete event has been parsed and is ready to be examined.\n\ +This is logically equivalent to the parse state immediately following\n\ +auparse_next_event()\n\ +\n\ +Returns None.\n\ +Raises exception (EnvironmentError) on error\n\ +"); +static PyObject * +AuParser_add_callback(AuParser *self, PyObject *args) +{ + PyObject *func; + PyObject *user_data; + + if (!PyArg_ParseTuple(args, "O|O:add_callback", &func, &user_data)) return NULL; + if (!PyFunction_Check(func)) { + PyErr_SetString(PyExc_ValueError, "callback must be a function"); + return NULL; + } + PARSER_CHECK; + + { + CallbackData *cb; + + cb = PyMem_New(CallbackData, 1); + if (cb == NULL) + return PyErr_NoMemory(); + cb->py_AuParser = self; + cb->func = func; + cb->user_data = user_data; + Py_INCREF(cb->func); + Py_XINCREF(cb->user_data); + auparse_add_callback(self->au, auparse_callback, cb, callback_data_destroy); +} + + Py_RETURN_NONE; +} + +/******************************** + * auparse_set_escape_mode + ********************************/ +PyDoc_STRVAR(set_escape_mode_doc, +"set_escape_mode() Set audit parser escaping\n\ +\n\ +This function sets the character escaping applied to value fields in the audit record.\n\ +Returns None.\n\ +"); +static PyObject * +AuParser_set_escape_mode(PyObject *args) +{ + int mode; + + if (!PyArg_ParseTuple(args, "i", &mode)) return NULL; + auparse_set_escape_mode(mode); + + return NULL; +} + +/******************************** + * auparse_reset + ********************************/ +PyDoc_STRVAR(reset_doc, +"reset() Reset audit parser instance\n\ +\n\ +reset resets all internal cursors to the beginning.\n\ +It closes files and descriptors.\n\ +\n\ +Returns None.\n\ +Raises exception (EnvironmentError) on error\n\ +"); +static PyObject * +AuParser_reset(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_reset(self->au); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * ausearch_add_expression + ********************************/ +PyDoc_STRVAR(search_add_expression_doc, +"search_add_expression(expression, how) Build up search expression\n\ +\n\ +\n\ +ausearch_add_item adds an expression to the current audit search\n\ +expression. The search conditions can then be used to scan logs,\n\ +files, or buffers for something of interest. The expression parameter\n\ +contains an expression, as specified in ausearch-expression(5).\n\ +\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ +\n\ +AUSEARCH_RULE_CLEAR:\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ +\n\ +AUSEARCH_RULE_OR:\n\ +\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ +\n\ +AUSEARCH_RULE_AND:\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ +\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_search_add_expression(AuParser *self, PyObject *args) +{ + const char *expression; + char *error; + int how; + int result; + + if (!PyArg_ParseTuple(args, "si", &expression, &how)) return NULL; + PARSER_CHECK; + + result = ausearch_add_expression(self->au, expression, &error, how); + if (result == 0) Py_RETURN_NONE; + if (error == NULL) + PyErr_SetFromErrno(PyExc_EnvironmentError); + else { + PyErr_SetString(PyExc_EnvironmentError, error); + free(error); + } + return NULL; +} + +/******************************** + * ausearch_add_item + ********************************/ +PyDoc_STRVAR(search_add_item_doc, +"search_add_item(field, op, value, how) Build up search rule\n\ +\n\ +\n\ +search_add_item() adds one search condition to the current audit search\n\ +expression. The search conditions can then be used to scan logs, files, or\n\ +buffers for something of interest. The field value is the field name\n\ +that the value will be checked for. The op variable describes what\n\ +kind of check is to be done. Legal op values are:\n\ +\n\ +'exists':\n\ +Just check that a field name exists\n\ +\n\ +'=':\n\ +locate the field name and check that the value associated with it\n\ +is equal to the value given in this rule.\n\ +\n\ +'!=':\n\ +locate the field name and check that the value associated with\n\ +it is NOT equal to the value given in this rule.\n\ +\n\ +The value parameter is compared to the uninterpreted field value.\n\ +\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ +\n\ +AUSEARCH_RULE_CLEAR:\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ +\n\ +AUSEARCH_RULE_OR:\n\ +\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ +\n\ +AUSEARCH_RULE_AND:\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ +\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); + +static PyObject * +AuParser_search_add_item(AuParser *self, PyObject *args) +{ + const char *field; + const char *op; + const char *value; + int how; + int result; + + if (!PyArg_ParseTuple(args, "sssi", &field, &op, &value, &how)) return NULL; + PARSER_CHECK; + + result = ausearch_add_item(self->au, field, op, value, how); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * ausearch_add_interpreted_item + ********************************/ +PyDoc_STRVAR(search_add_interpreted_item_doc, +"search_add_interpreted_item(field, op, value, how) Build up search rule\n\ +\n\ +\n\ +search_add_interpreted_item() adds one search condition to the current audit\n\ +search expression. The search conditions can then be used to scan logs,\n\ +files, or buffers for something of interest. The field value is the field\n\ +name that the value will be checked for. The op variable describes what\n\ +kind of check is to be done. Legal op values are:\n\ +\n\ +'exists':\n\ +Just check that a field name exists\n\ +\n\ +'=':\n\ +locate the field name and check that the value associated with it\n\ +is equal to the value given in this rule.\n\ +\n\ +'!=':\n\ +locate the field name and check that the value associated with\n\ +it is NOT equal to the value given in this rule.\n\ +\n\ +The value parameter is compared to the interpreted field value (the value\n\ +that would be returned by AuParser.interpret_field).\n\ +\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ +\n\ +AUSEARCH_RULE_CLEAR:\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ +\n\ +AUSEARCH_RULE_OR:\n\ +\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ +\n\ +AUSEARCH_RULE_AND:\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ +\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); + +static PyObject * +AuParser_search_add_interpreted_item(AuParser *self, PyObject *args) +{ + const char *field; + const char *op; + const char *value; + int how; + int result; + + if (!PyArg_ParseTuple(args, "sssi", &field, &op, &value, &how)) return NULL; + PARSER_CHECK; + + result = ausearch_add_interpreted_item(self->au, field, op, value, how); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * ausearch_add_timestamp_item + ********************************/ +PyDoc_STRVAR(search_add_timestamp_item_doc, +"search_add_timestamp_item(op, sec, milli, how) Build up search rule\n\ +\n\ +\n\ +search_add_timestamp_item adds an event time condition to the current audit\n\ +search expression. The search conditions can then be used to scan logs,\n\ +files, or buffers for something of interest. The op parameter specifies the\n\ +desired comparison. Legal op values are \"<\", \"<=\", \">=\", \">\" and\n\ +\"=\". The left operand of the comparison operator is the timestamp of the\n\ +examined event, the right operand is specified by the sec and milli\n\ +parameters.\n\ +\n\ +The how parameter determines how this search expression will affect the\n\ +existing search expression, if one is already defined. The possible\n\ +values are:\n\ +\n\ +AUSEARCH_RULE_CLEAR:\n\ +Clear the current search expression, if any, and use only this search\n\ +expression.\n\ +\n\ +AUSEARCH_RULE_OR:\n\ +\n\ +If a search expression E is already configured, replace it by\n\ +(E || this_search_expression).\n\ +\n\ +AUSEARCH_RULE_AND:\n\ +If a search expression E is already configured, replace it by\n\ +(E && this_search_expression).\n\ +\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); + +static PyObject * +AuParser_search_add_timestamp_item(AuParser *self, PyObject *args) +{ + const char *op; + PY_LONG_LONG sec; + int milli; + int how; + int result; + + /* There's no completely portable way to handle time_t values from Python; + note that time_t might even be a floating-point type! PY_LONG_LONG + is at least enough not to worry about year 2038. + + milli is int because Python's 'I' format does no overflow checking. + Negative milli values will wrap to values > 1000 and + ausearch_add_timestamp_item will reject them. */ + if (!PyArg_ParseTuple(args, "sLii", &op, &sec, &milli, &how)) + return NULL; + PARSER_CHECK; + + result = ausearch_add_timestamp_item(self->au, op, sec, (unsigned)milli, + how); + if (result == 0) + Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * ausearch_add_timestamp_item_ex + ********************************/ +PyDoc_STRVAR(search_add_timestamp_item_ex_doc, +"search_add_timestamp_item_ex(op, sec, milli, serial, how) Build up search rule\n\ +search_add_timestamp_item_ex adds an event time condition to the current audit\n\ +search expression. Its similar to search_add_timestamp_item except it adds\n\ +the event serial number.\n\ +"); + +static PyObject * +AuParser_search_add_timestamp_item_ex(AuParser *self, PyObject *args) +{ + const char *op; + PY_LONG_LONG sec; + int milli; + int serial; + int how; + int result; + + /* There's no completely portable way to handle time_t values from Python; + note that time_t might even be a floating-point type! PY_LONG_LONG + is at least enough not to worry about year 2038. + + milli is int because Python's 'I' format does no overflow checking. + Negative milli values will wrap to values > 1000 and + ausearch_add_timestamp_item will reject them. */ + if (!PyArg_ParseTuple(args, "sLiiii", &op, &sec, &milli, &serial, &how)) + return NULL; + PARSER_CHECK; + + result = ausearch_add_timestamp_item_ex(self->au, op, sec, (unsigned)milli, + (unsigned)serial, how); + if (result == 0) + Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * ausearch_add_regex + ********************************/ +PyDoc_STRVAR(search_add_regex_doc, +"search_add_regex(regexp) Add a regular expression to the search criteria.\n\ +\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_search_add_regex(AuParser *self, PyObject *args) +{ + const char* regexp; + int result; + + if (!PyArg_ParseTuple(args, "s", ®exp)) return NULL; + PARSER_CHECK; + result = ausearch_add_regex(self->au, regexp); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * ausearch_set_stop + ********************************/ +PyDoc_STRVAR(search_set_stop_doc, +"search_set_stop(where) Set where cursor is positioned on search match.\n\ +\n\ +search_set_stop() determines where the internal cursor will stop when\n\ +a search condition is met. The possible values are:\n\ +\n\ +AUSEARCH_STOP_EVENT:\n\ +This one repositions the cursors to the first field of the first\n\ +record of the event con- taining the items searched for.\n\ +\n\ +AUSEARCH_STOP_RECORD:\n\ +This one repositions the cursors to the first field of the record\n\ +containing the items searched for.\n\ +\n\ +AUSEARCH_STOP_FIELD:\n\ +This one simply stops on the current field when the evaluation of the\n\ +rules becomes true.\n\ +\n\ +No Return value, raises exception (ValueError) on error.\n\ +"); +static PyObject * +AuParser_search_set_stop(AuParser *self, PyObject *args) +{ + int where; + int result; + + if (!PyArg_ParseTuple(args, "i", &where)) return NULL; + PARSER_CHECK; + result = ausearch_set_stop(self->au, where); + if (result == 0) Py_RETURN_NONE; + PyErr_SetFromErrno(PyExc_ValueError); + return NULL; +} + +/******************************** + * ausearch_clear + ********************************/ +PyDoc_STRVAR(search_clear_doc, +"search_clear() Clear search parameters.\n\ +\n\ +ausearch_clear clears any search parameters stored in the parser\n\ +instance and frees memory associated with it.\n\ +\n\ +No Return value.\n\ +"); +static PyObject * +AuParser_search_clear(AuParser *self) +{ + PARSER_CHECK; + ausearch_clear(self->au); + Py_RETURN_NONE; +} + +/******************************** + * ausearch_next_event + ********************************/ +PyDoc_STRVAR(search_next_event_doc, +"search_next_event() Find the next event that meets search criteria.\n\ +\n\ +search_next_event() will scan the input source and evaluate whether\n\ +any record in an event contains the data being searched\n\ +for. Evaluation is done at the record level.\n\ +\n\ +Returns True if a match was found\n\ +Returns False if a match was not found.\n\ +\n\ +Raises exception (EnvironmentError) on error\n\ +"); +static PyObject * +AuParser_search_next_event(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = ausearch_next_event(self->au); + if (result > 0) Py_RETURN_TRUE; + if (result == 0) Py_RETURN_FALSE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_next_event + ********************************/ +PyDoc_STRVAR(parse_next_event_doc, +"parse_next_event() Advance the parser to the next event.\n\ +\n\ +parse_next_event() will position the cursors at the first field of the first\n\ +record of the next event in a file or buffer. It does not skip events\n\ +or honor any search criteria that may be stored.\n\ +\n\ +Returns True if parser advances to next event.\n\ +Returns False if there are no more events to parse\n\ +\n\ +Raises exception (EnvironmentError) on error\n\ +"); +static PyObject * +AuParser_parse_next_event(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_next_event(self->au); + if (result > 0) Py_RETURN_TRUE; + if (result == 0) Py_RETURN_FALSE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_get_timestamp + ********************************/ +PyDoc_STRVAR(get_timestamp_doc, +"get_timestamp() Return current event's timestamp.\n\ +\n\ +Returns the current event's timestamp info as an AuEvent object.\n\ +No Return value, raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_get_timestamp(AuParser *self) +{ + const au_event_t *event_ptr; + PyObject *py_event; + + PARSER_CHECK; + event_ptr = auparse_get_timestamp(self->au); + + if (event_ptr == NULL) { + if (errno) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; + } else { + Py_RETURN_NONE; + } + } + py_event = AuEvent_new_from_struct(event_ptr); + Py_INCREF(py_event); /* FIXME: should we be bumping the ref count? */ + return py_event; +} + +/******************************** + * auparse_get_num_records + ********************************/ +PyDoc_STRVAR(get_num_records_doc, +"get_num_records() Get the number of records.\n\ +\n\ +Returns the number of records in the current event.\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_get_num_records(AuParser *self) +{ + int num_records; + + PARSER_CHECK; + num_records = auparse_get_num_records(self->au); + if (num_records == 0) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; + } + return Py_BuildValue("i", num_records); +} + +/******************************** + * auparse_first_record + ********************************/ +PyDoc_STRVAR(first_record_doc, +"first_record() Reposition record cursor.\n\ +\n\ +first_record() repositions the internal cursors of the parsing library\n\ +to point to the first record in the current event.\n\ +\n\ +Return True for success, False if there is no event data.\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_first_record(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_first_record(self->au); + if (result > 0) Py_RETURN_TRUE; + if (result == 0) Py_RETURN_FALSE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_next_record + ********************************/ +PyDoc_STRVAR(next_record_doc, +"next_record() Advance record cursor.\n\ +\n\ +next_record() will move the internal library cursors to point to the\n\ +next record of the current event.\n\ +\n\ +Returns True on success, False if no more records in current event\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_next_record(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_next_record(self->au); + + if (result > 0) Py_RETURN_TRUE; + if (result == 0) Py_RETURN_FALSE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_goto_record_num + ********************************/ +PyDoc_STRVAR(goto_record_num_doc, +"goto_record_num() Move record cursor to specific position.\n\ +\n\ +goto_record_num() will move the internal library cursors to point\n\ +to a specific physical record number. Records within the same event are\n\ +numbered starting from 0. This is generally not needed but there are\n\ +some cases where one may want precise control over the exact record\n\ +being looked at.\n\ +\n\ +Returns True on success, False if no more records in current event\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_goto_record_num(AuParser *self, PyObject *args) +{ + int result; + unsigned int num; + + if (!PyArg_ParseTuple(args, "i", &num)) return NULL; + PARSER_CHECK; + result = auparse_goto_record_num(self->au, num); + + if (result > 0) Py_RETURN_TRUE; + if (result == 0) Py_RETURN_FALSE; + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; +} + +/******************************** + * auparse_get_type + ********************************/ +PyDoc_STRVAR(get_type_doc, +"get_type() Get record’s type.\n\ +\n\ +get_type() will return the integer value for the current record of the\n\ +current event.\n\ +\n\ +Returns record type.\n\ +Raises exception (LookupError) on error.\n\ +"); +static PyObject * +AuParser_get_type(AuParser *self) +{ + int value; + + PARSER_CHECK; + value = auparse_get_type(self->au); + + if (value == 0) { + PyErr_SetString(PyExc_LookupError, "Not found"); + return NULL; + } + return Py_BuildValue("i", value); +} + +/******************************** + * auparse_get_type_name + ********************************/ +PyDoc_STRVAR(get_type_name_doc, +"get_type_name() Get current record’s type name.\n\ +\n\ +get_type_name() allows access to the current record type name in the\n\ +current event.\n\ +\n\ +Returns None if the record type name is unavailable.\n\ +"); +static PyObject * +AuParser_get_type_name(AuParser *self) +{ + const char *name = NULL; + + PARSER_CHECK; + name = auparse_get_type_name(self->au); + return Py_BuildValue("s", name); +} + +/******************************** + * auparse_get_line_number + ********************************/ +PyDoc_STRVAR(get_line_number_doc, +"auparse_get_line_number() get line number where record was found\n\ +\n\ +get_line_number will return the source input line number for\n\ +the current record of the current event. Line numbers start at 1. If\n\ +the source input type is AUSOURCE_FILE_ARRAY the line numbering will\n\ +reset back to 1 each time a new life in the file array is opened.\n\ +"); +static PyObject * +AuParser_get_line_number(AuParser *self) +{ + unsigned int value; + + PARSER_CHECK; + value = auparse_get_line_number(self->au); + return Py_BuildValue("I", value); +} + +/******************************** + * auparse_get_filename + ********************************/ +PyDoc_STRVAR(get_filename_doc, +"auparse_get_filename() get the filename where record was found\n\ +get_filename() will return the name of the source file where the\n\ +record was found if the source type is AUSOURCE_FILE or\n\ +AUSOURCE_FILE_ARRAY. For other source types the return value will be\n\ +None.\n\ +"); +static PyObject * +AuParser_get_filename(AuParser *self) +{ + const char *value; + + PARSER_CHECK; + value = auparse_get_filename(self->au); + + if (value == NULL) Py_RETURN_NONE; + return Py_BuildValue("s", value); +} + +/******************************** + * auparse_first_field + ********************************/ +PyDoc_STRVAR(first_field_doc, +"first_field() Reposition field cursor.\n\ +\n\ +Returns True on success, False if there is no event data\n\ +"); +static PyObject * +AuParser_first_field(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_first_field(self->au); + + if (result == 0) Py_RETURN_FALSE; + Py_RETURN_TRUE; +} + +/******************************** + * auparse_next_field + ********************************/ +PyDoc_STRVAR(next_field_doc, +"next_field() Advance the field cursor.\n\ +\n\ +next_field() moves the library’s internal cursor to point to the next\n\ +field in the current record of the current event.\n\ +\n\ +Returns True on success, False if there is no more fields exist\n\ +"); +static PyObject * +AuParser_next_field(AuParser *self) +{ + int result; + + PARSER_CHECK; + result = auparse_next_field(self->au); + + if (result == 0) Py_RETURN_FALSE; + Py_RETURN_TRUE; +} + +/******************************** + * auparse_get_num_fields + ********************************/ +PyDoc_STRVAR(get_num_fields_doc, +"get_num_fields() Get the number of fields.\n\ +\n\ +Returns the number of fields in the current event.\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_get_num_fields(AuParser *self) +{ + int num_fields; + + PARSER_CHECK; + num_fields = auparse_get_num_fields(self->au); + if (num_fields == 0) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; + } + return Py_BuildValue("i", num_fields); +} + +/******************************** + * auparse_get_record_text + ********************************/ +PyDoc_STRVAR(get_record_text_doc, +"get_record_text() Return unparsed record data\n\ +\n\ +get_record_text() returns the full unparsed record.\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_get_record_text(AuParser *self) +{ + const char *text; + + PARSER_CHECK; + text = auparse_get_record_text(self->au); + + if (text == NULL) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; + } + return Py_BuildValue("s", text); +} + +/******************************** + * auparse_find_field + ********************************/ +PyDoc_STRVAR(find_field_doc, +"find_field(name) Search for field name.\n\ +\n\ +find_field() will scan all records in an event to find the first\n\ +occurance of the field name passed to it. Searching begins from the\n\ +cursor’s current position. The field name is stored for subsequent\n\ +searching.\n\ +\n\ +Returns value associated with field or None if not found.\n\ +"); +static PyObject * +AuParser_find_field(AuParser *self, PyObject *args) +{ + char *name = NULL; + const char *value; + + if (!PyArg_ParseTuple(args, "s:find_field", &name)) return NULL; + PARSER_CHECK; + if ((value =auparse_find_field(self->au, name)) == NULL) { + if (errno) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; + } else { + Py_RETURN_NONE; + } + } + return Py_BuildValue("s", value); +} + +const char *auparse_find_field_next(auparse_state_t *au); +/******************************** + * auparse_find_field_next + ********************************/ +PyDoc_STRVAR(find_field_next_doc, +"find_field_next() Get next occurrance of field name\n\ +\n\ +find_field_next() returns the value associated next occurrance of field name.\n\ +Returns value associated with field or None if there is no next field.\n\ +Raises exception (EnvironmentError) on error.\n\ +"); +static PyObject * +AuParser_find_field_next(AuParser *self) +{ + const char *value; + + PARSER_CHECK; + if ((value = auparse_find_field_next(self->au)) == NULL) { + if (errno) { + PyErr_SetFromErrno(PyExc_EnvironmentError); + return NULL; + } else { + Py_RETURN_NONE; + } + } + return Py_BuildValue("s", value); +} + +/******************************** + * auparse_get_field_name + ********************************/ +PyDoc_STRVAR(get_field_name_doc, +"get_field_name() Get current field’s name.\n\ +\n\ +get_field_name() allows access to the current field name of the\n\ +current record in the current event.\n\ +\n\ +Returns None if the field value is unavailable.\n\ +"); +static PyObject * +AuParser_get_field_name(AuParser *self) +{ + const char *name = NULL; + + PARSER_CHECK; + name = auparse_get_field_name(self->au); + return Py_BuildValue("s", name); +} + +/******************************** + * auparse_get_field_str + ********************************/ +PyDoc_STRVAR(get_field_str_doc, +"get_field_str() get current field’s value\n\ +\n\ +get_field_str() allows access to the value in the current field of the\n\ +current record in the current event.\n\ +\n\ +Returns None if the field value is unavailable.\n\ +"); +static PyObject * +AuParser_get_field_str(AuParser *self) +{ + const char *value = NULL; + + PARSER_CHECK; + value = auparse_get_field_str(self->au); + return Py_BuildValue("s", value); +} + +/******************************** + * auparse_get_field_type + ********************************/ +PyDoc_STRVAR(get_field_type_doc, +"get_field_type() Get current field’s data type value.\n\ +\n\ +get_field_type() returns a value from the auparse_type_t enum that\n\ +describes the kind of data in the current field of the current record\n\ +in the current event.\n\ +\n\ +Returns AUPARSE_TYPE_UNCLASSIFIED if the field’s data type has no\n\ +known description or is an integer. Otherwise it returns another enum.\n\ +Fields with the type AUPARSE_TYPE_ESCAPED must be interpretted to access\n\ +their value since those field’s raw value is encoded.\n\ +"); +static PyObject * +AuParser_get_field_type(AuParser *self) +{ + int value; + + PARSER_CHECK; + value = auparse_get_field_type(self->au); + return Py_BuildValue("i", value); +} + +/******************************** + * auparse_get_field_int + ********************************/ +PyDoc_STRVAR(get_field_int_doc, +"get_field_int() Get current field’s value as an integer.\n\ +\n\ +get_field_int() allows access to the value as an int of the current\n\ +field of the current record in the current event.\n\ +\n\ +Returns None if the field value is unavailable.\n\ +"); +static PyObject * +AuParser_get_field_int(AuParser *self) +{ + int value; + + PARSER_CHECK; + value = auparse_get_field_int(self->au); + if (errno == 0) return Py_BuildValue("i", value); + Py_RETURN_NONE; +} + +// FIXME: can't tell if interpret is succesful, always returns some string in somewhat arbitrary format. +PyDoc_STRVAR(interpret_field_doc, +"interpret_field() Return an interpretation of the current field as a string that has the chosen character escaping applied.\n\ +\n\ +If the field cannot be interpreted the field is returned unmodified.\n\ +Returns None if the field value is unavailable.\n\ +"); +static PyObject * +AuParser_interpret_field(AuParser *self) +{ + const char *value = NULL; + + PARSER_CHECK; + value = auparse_interpret_field(self->au); + return Py_BuildValue("s", value); +} + +static +PyGetSetDef AuParser_getseters[] = { + {NULL} /* Sentinel */ +}; + +static +PyMemberDef AuParser_members[] = { + {NULL} /* Sentinel */ +}; + +static PyMethodDef AuParser_methods[] = { + {"feed", (PyCFunction)AuParser_feed, METH_VARARGS, feed_doc}, + {"flush_feed", (PyCFunction)AuParser_flush_feed, METH_NOARGS, flush_feed_doc}, + {"add_callback", (PyCFunction)AuParser_add_callback, METH_VARARGS, add_callback_doc}, + {"set_escape_mode", (PyCFunction)AuParser_set_escape_mode, METH_VARARGS, set_escape_mode_doc}, + {"reset", (PyCFunction)AuParser_reset, METH_NOARGS, reset_doc}, + {"search_add_expression", (PyCFunction)AuParser_search_add_expression, METH_VARARGS, search_add_expression_doc}, + {"search_add_item", (PyCFunction)AuParser_search_add_item, METH_VARARGS, search_add_item_doc}, + {"search_add_interpreted_item", (PyCFunction)AuParser_search_add_interpreted_item, METH_VARARGS, search_add_interpreted_item_doc}, + {"search_add_timestamp_item", (PyCFunction)AuParser_search_add_timestamp_item, METH_VARARGS, search_add_timestamp_item_doc}, + {"search_add_timestamp_item_ex", (PyCFunction)AuParser_search_add_timestamp_item_ex, METH_VARARGS, search_add_timestamp_item_ex_doc}, + {"search_add_regex", (PyCFunction)AuParser_search_add_regex, METH_VARARGS, search_add_regex_doc}, + {"search_set_stop", (PyCFunction)AuParser_search_set_stop, METH_VARARGS, search_set_stop_doc}, + {"search_clear", (PyCFunction)AuParser_search_clear, METH_NOARGS, search_clear_doc}, + {"search_next_event", (PyCFunction)AuParser_search_next_event, METH_NOARGS, search_next_event_doc}, + {"parse_next_event", (PyCFunction)AuParser_parse_next_event, METH_NOARGS, parse_next_event_doc}, + {"get_timestamp", (PyCFunction)AuParser_get_timestamp, METH_NOARGS, get_timestamp_doc}, + {"get_num_records", (PyCFunction)AuParser_get_num_records, METH_NOARGS, get_num_records_doc}, + {"first_record", (PyCFunction)AuParser_first_record, METH_NOARGS, first_record_doc}, + {"next_record", (PyCFunction)AuParser_next_record, METH_NOARGS, next_record_doc}, + {"goto_record_num", (PyCFunction)AuParser_goto_record_num, METH_VARARGS, goto_record_num_doc}, + {"get_type", (PyCFunction)AuParser_get_type, METH_NOARGS, get_type_doc}, + {"get_type_name", (PyCFunction)AuParser_get_type_name, METH_NOARGS, get_type_name_doc}, + {"get_line_number", (PyCFunction)AuParser_get_line_number, METH_NOARGS, get_line_number_doc}, + {"get_filename", (PyCFunction)AuParser_get_filename, METH_NOARGS, get_filename_doc}, + {"first_field", (PyCFunction)AuParser_first_field, METH_NOARGS, first_field_doc}, + {"next_field", (PyCFunction)AuParser_next_field, METH_NOARGS, next_field_doc}, + {"get_num_fields", (PyCFunction)AuParser_get_num_fields, METH_NOARGS, get_num_fields_doc}, + {"get_record_text", (PyCFunction)AuParser_get_record_text, METH_NOARGS, get_record_text_doc}, + {"find_field_next", (PyCFunction)AuParser_find_field_next, METH_NOARGS, find_field_next_doc}, + {"find_field", (PyCFunction)AuParser_find_field, METH_VARARGS, find_field_doc}, + {"get_field_name", (PyCFunction)AuParser_get_field_name, METH_NOARGS, get_field_name_doc}, + {"get_field_str", (PyCFunction)AuParser_get_field_str, METH_NOARGS, get_field_str_doc}, + {"get_field_type", (PyCFunction)AuParser_get_field_type, METH_NOARGS, get_field_type_doc}, + {"get_field_int", (PyCFunction)AuParser_get_field_int, METH_NOARGS, get_field_int_doc}, + {"interpret_field", (PyCFunction)AuParser_interpret_field, METH_NOARGS, interpret_field_doc}, + {NULL, NULL} /* Sentinel */ +}; + +PyDoc_STRVAR(AuParser_doc, +"AuParser(source_type, source)\n\ +\n\ +Construct a new audit parser object and bind it to input data.\n\ +source_type: one of the AUSOURCE_* constants.\n\ +source: the input data, dependent on the source_type as follows:\n\ +\n\ +AUSOURCE_LOGS: None (system log files will be parsed)\n\ +AUSOURCE_FILE: string containing file path name\n\ +AUSOURCE_FILE_ARRAY: list or tuple of strings each containing a file path name\n\ +AUSOURCE_BUFFER: string containing audit data to parse\n\ +AUSOURCE_BUFFER_ARRAY: list or tuple of strings each containing audit data to parse\n\ +AUSOURCE_DESCRIPTOR: integer file descriptor (e.g. fileno)\n\ +AUSOURCE_FILE_POINTER: file object (e.g. types.FileType)\n\ +AUSOURCE_FEED: None (data supplied via feed()\n\ +"); + +static PyTypeObject AuParserType = { + PyVarObject_HEAD_INIT(NULL, 0) + "auparse.AuParser", /*tp_name*/ + sizeof(AuParser), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)AuParser_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + AuParser_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + AuParser_methods, /* tp_methods */ + AuParser_members, /* tp_members */ + AuParser_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)AuParser_init, /* tp_init */ + 0, /* tp_alloc */ + AuParser_new, /* tp_new */ +}; + + + +/*=========================================================================== + * Module + *===========================================================================*/ + +PyDoc_STRVAR(auparse_doc, +"Parsing library for audit messages.\n\ +\n\ +The module defines the following exceptions:\n\ +\n\ +NoParser: Raised if the underlying C code parser is not bound to the AuParser object.\n\ +"); + +static PyMethodDef module_methods[] = { + {NULL} /* Sentinel */ +}; + +#ifdef IS_PY3K +static struct PyModuleDef auparse_def = { + PyModuleDef_HEAD_INIT, + "auparse", + NULL, + -1, + module_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_auparse(void) +#else +PyMODINIT_FUNC +initauparse(void) +#endif +{ + PyObject* m; + + if (PyType_Ready(&AuEventType) < 0) MODINITERROR; + if (PyType_Ready(&AuParserType) < 0) MODINITERROR; + +#ifdef IS_PY3K + m = PyModule_Create(&auparse_def); +#else + m = Py_InitModule3("auparse", module_methods, auparse_doc); +#endif + + if (m == NULL) + MODINITERROR; + + Py_INCREF(&AuParserType); + PyModule_AddObject(m, "AuParser", (PyObject *)&AuParserType); + + Py_INCREF(&AuEventType); + PyModule_AddObject(m, "AuEvent", (PyObject *)&AuEventType); + + /* exceptions */ + NoParserError = PyErr_NewException("auparse.NoParser", NULL, NULL); + Py_INCREF(NoParserError); + PyModule_AddObject(m, "NoParser", NoParserError); + + /* ausource_t */ + PyModule_AddIntConstant(m, "AUSOURCE_LOGS", AUSOURCE_LOGS); + PyModule_AddIntConstant(m, "AUSOURCE_FILE", AUSOURCE_FILE); + PyModule_AddIntConstant(m, "AUSOURCE_FILE_ARRAY", AUSOURCE_FILE_ARRAY); + PyModule_AddIntConstant(m, "AUSOURCE_BUFFER", AUSOURCE_BUFFER); + PyModule_AddIntConstant(m, "AUSOURCE_BUFFER_ARRAY", AUSOURCE_BUFFER_ARRAY); + PyModule_AddIntConstant(m, "AUSOURCE_DESCRIPTOR", AUSOURCE_DESCRIPTOR); + PyModule_AddIntConstant(m, "AUSOURCE_FILE_POINTER", AUSOURCE_FILE_POINTER); + PyModule_AddIntConstant(m, "AUSOURCE_FEED", AUSOURCE_FEED); + + /* ausearch_op_t */ + PyModule_AddIntConstant(m, "AUSEARCH_UNSET", AUSEARCH_UNSET); + PyModule_AddIntConstant(m, "AUSEARCH_EXISTS", AUSEARCH_EXISTS); + PyModule_AddIntConstant(m, "AUSEARCH_EQUAL", AUSEARCH_EQUAL); + PyModule_AddIntConstant(m, "AUSEARCH_NOT_EQUAL", AUSEARCH_NOT_EQUAL); + PyModule_AddIntConstant(m, "AUSEARCH_TIME_LT", AUSEARCH_TIME_LT); + PyModule_AddIntConstant(m, "AUSEARCH_TIME_LE", AUSEARCH_TIME_LE); + PyModule_AddIntConstant(m, "AUSEARCH_TIME_GE", AUSEARCH_TIME_GE); + PyModule_AddIntConstant(m, "AUSEARCH_TIME_GT", AUSEARCH_TIME_GT); + PyModule_AddIntConstant(m, "AUSEARCH_TIME_EQ", AUSEARCH_TIME_EQ); + PyModule_AddIntConstant(m, "AUSEARCH_INTERPRETED", 0x40000000); + + /* austop_t */ + PyModule_AddIntConstant(m, "AUSEARCH_STOP_EVENT", AUSEARCH_STOP_EVENT); + PyModule_AddIntConstant(m, "AUSEARCH_STOP_RECORD", AUSEARCH_STOP_RECORD); + PyModule_AddIntConstant(m, "AUSEARCH_STOP_FIELD", AUSEARCH_STOP_FIELD); + + /* ausearch_rule_t */ + PyModule_AddIntConstant(m, "AUSEARCH_RULE_CLEAR", AUSEARCH_RULE_CLEAR); + PyModule_AddIntConstant(m, "AUSEARCH_RULE_OR", AUSEARCH_RULE_OR); + PyModule_AddIntConstant(m, "AUSEARCH_RULE_AND", AUSEARCH_RULE_AND); + PyModule_AddIntConstant(m, "AUSEARCH_RULE_REGEX", AUSEARCH_RULE_REGEX); + + /* auparse_cb_event_t */ + PyModule_AddIntConstant(m, "AUPARSE_CB_EVENT_READY", AUPARSE_CB_EVENT_READY); + /* auparse_type_t */ + PyModule_AddIntConstant(m, "AUPARSE_TYPE_UNCLASSIFIED", AUPARSE_TYPE_UNCLASSIFIED); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_UID", AUPARSE_TYPE_UID); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_GID", AUPARSE_TYPE_GID); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_SYSCALL", AUPARSE_TYPE_SYSCALL); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_ARCH", AUPARSE_TYPE_ARCH); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_EXIT", AUPARSE_TYPE_EXIT); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_ESCAPED", AUPARSE_TYPE_ESCAPED); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_PERM", AUPARSE_TYPE_PERM); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_MODE", AUPARSE_TYPE_MODE); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_SOCKADDR", AUPARSE_TYPE_SOCKADDR); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_FLAGS", AUPARSE_TYPE_FLAGS); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_PROMISC", AUPARSE_TYPE_PROMISC); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_CAPABILITY", AUPARSE_TYPE_CAPABILITY); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_SUCCESS", AUPARSE_TYPE_SUCCESS); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_A0", AUPARSE_TYPE_A0); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_A1", AUPARSE_TYPE_A1); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_A2", AUPARSE_TYPE_A2); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_SIGNAL", AUPARSE_TYPE_SIGNAL); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_LIST", AUPARSE_TYPE_LIST); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_TTY_DATA", AUPARSE_TYPE_TTY_DATA); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_SESSION", AUPARSE_TYPE_SESSION); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_CAP_BITMAP", AUPARSE_TYPE_CAP_BITMAP); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_NFPROTO", AUPARSE_TYPE_NFPROTO); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_ICMPTYPE", AUPARSE_TYPE_ICMPTYPE); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_PROTOCOL", AUPARSE_TYPE_PROTOCOL); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_ADDR", AUPARSE_TYPE_ADDR); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_PERSONALITY", AUPARSE_TYPE_PERSONALITY); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_SECCOMP", AUPARSE_TYPE_SECCOMP); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_OFLAG", AUPARSE_TYPE_OFLAG); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_MMAP", AUPARSE_TYPE_MMAP); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_MODE_SHORT", AUPARSE_TYPE_MODE_SHORT); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_MAC_LABEL", AUPARSE_TYPE_MAC_LABEL); + PyModule_AddIntConstant(m, "AUPARSE_TYPE_PROCTITLE", AUPARSE_TYPE_PROCTITLE); + + /* Escape types */ + PyModule_AddIntConstant(m, "AUPARSE_ESC_RAW", AUPARSE_ESC_RAW); + PyModule_AddIntConstant(m, "AUPARSE_ESC_TTY", AUPARSE_ESC_TTY); + PyModule_AddIntConstant(m, "AUPARSE_ESC_SHELL", AUPARSE_ESC_SHELL); + PyModule_AddIntConstant(m, "AUPARSE_ESC_SHELL_QUOTE", AUPARSE_ESC_SHELL_QUOTE); + +#ifdef IS_PY3K + return m; +#endif +} diff --git a/framework/src/audit/bindings/python/python2/Makefile.am b/framework/src/audit/bindings/python/python2/Makefile.am new file mode 100644 index 00000000..1dcb5bca --- /dev/null +++ b/framework/src/audit/bindings/python/python2/Makefile.am @@ -0,0 +1,33 @@ +# Makefile.am -- +# Copyright 2007,2008,2011 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# Miloslav Trmac +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AM_CFLAGS = -fPIC -DPIC -fno-strict-aliasing +AM_CPPFLAGS = -I$(top_builddir) -I@PYINCLUDEDIR@ + +pyexec_LTLIBRARIES = auparse.la + +auparse_la_SOURCES = $(top_srcdir)/bindings/python/auparse_python.c +auparse_la_CPPFLAGS = -I$(top_srcdir)/auparse $(AM_CPPFLAGS) +auparse_la_LDFLAGS = -module -avoid-version -Wl,-z,relro +auparse_la_LIBADD = ${top_builddir}/auparse/libauparse.la ${top_builddir}/lib/libaudit.la diff --git a/framework/src/audit/bindings/python/python3/Makefile.am b/framework/src/audit/bindings/python/python3/Makefile.am new file mode 100644 index 00000000..edd38e9f --- /dev/null +++ b/framework/src/audit/bindings/python/python3/Makefile.am @@ -0,0 +1,32 @@ +# Makefile.am -- +# Copyright 2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AM_CFLAGS = -fPIC -DPIC -fno-strict-aliasing $(PYTHON3_CFLAGS) +AM_CPPFLAGS = -I$(top_builddir) $(PYTHON3_INCLUDES) + +py3exec_LTLIBRARIES = auparse.la + +auparse_la_SOURCES = $(top_srcdir)/bindings/python/auparse_python.c +auparse_la_CPPFLAGS = -I$(top_srcdir)/auparse $(AM_CPPFLAGS) +auparse_la_LDFLAGS = -module -avoid-version -Wl,-z,relro +auparse_la_LIBADD = ${top_builddir}/auparse/libauparse.la ${top_builddir}/lib/libaudit.la diff --git a/framework/src/audit/bindings/python/setup.py b/framework/src/audit/bindings/python/setup.py new file mode 100644 index 00000000..e69de29b diff --git a/framework/src/audit/bindings/swig/Makefile.am b/framework/src/audit/bindings/swig/Makefile.am new file mode 100644 index 00000000..29355022 --- /dev/null +++ b/framework/src/audit/bindings/swig/Makefile.am @@ -0,0 +1,33 @@ +# Makefile.am -- +# Copyright 2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +EXTRA_DIST = src/auditswig.i + +SUBDIRS = src +if HAVE_PYTHON +SUBDIRS += python +endif +if USE_PYTHON3 +SUBDIRS += python3 +endif + diff --git a/framework/src/audit/bindings/swig/python/Makefile.am b/framework/src/audit/bindings/swig/python/Makefile.am new file mode 100644 index 00000000..8c98b943 --- /dev/null +++ b/framework/src/audit/bindings/swig/python/Makefile.am @@ -0,0 +1,40 @@ +# Makefile.am -- +# Copyright 2005,2007,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AM_CFLAGS = -fPIC -DPIC -fno-strict-aliasing +AM_CPPFLAGS = -I. -I$(top_builddir) -I${top_srcdir}/lib -I@PYINCLUDEDIR@ +SWIG_FLAGS = -python +SWIG_INCLUDES = -I. -I$(top_builddir) -I${top_srcdir}/lib -I@PYINCLUDEDIR@ +pyexec_PYTHON = audit.py +pyexec_LTLIBRARIES = _audit.la +pyexec_SOLIBRARIES = _audit.so +_audit_la_CFLAGS = -shared +_audit_la_LDFLAGS = -module -avoid-version -Wl,-z,relro +_audit_la_HEADERS: $(top_builddir)/config.h +_audit_la_DEPENDENCIES =${top_srcdir}/lib/libaudit.h ${top_builddir}/lib/libaudit.la +_audit_la_LIBADD = $(top_builddir)/lib/libaudit.la +nodist__audit_la_SOURCES = audit_wrap.c +audit.py audit_wrap.c: ${srcdir}/../src/auditswig.i + swig -o audit_wrap.c ${SWIG_FLAGS} ${SWIG_INCLUDES} ${srcdir}/../src/auditswig.i + +CLEANFILES = audit.py* audit_wrap.c *~ + diff --git a/framework/src/audit/bindings/swig/python3/Makefile.am b/framework/src/audit/bindings/swig/python3/Makefile.am new file mode 100644 index 00000000..036b5edb --- /dev/null +++ b/framework/src/audit/bindings/swig/python3/Makefile.am @@ -0,0 +1,41 @@ +# Makefile.am -- +# Copyright 2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AM_CFLAGS = -fPIC -DPIC -fno-strict-aliasing $(PYTHON3_CFLAGS) +AM_CPPFLAGS = -I. -I$(top_builddir) -I${top_srcdir}/lib $(PYTHON3_INCLUDES) +LIBS = $(top_builddir)/lib/libaudit.la +SWIG_FLAGS = -python -py3 -modern +SWIG_INCLUDES = -I. -I$(top_builddir) -I${top_srcdir}/lib $(PYTHON3_INCLUDES) +py3exec_PYTHON = audit.py +py3exec_LTLIBRARIES = _audit.la +py3exec_SOLIBRARIES = _audit.so +_audit_la_CFLAGS = -shared +_audit_la_LDFLAGS = -module -avoid-version -Wl,-z,relro +_audit_la_HEADERS: $(top_builddir)/config.h +_audit_la_DEPENDENCIES =${top_srcdir}/lib/libaudit.h ${top_builddir}/lib/libaudit.la +_audit_la_LIBADD = ${top_builddir}/lib/libaudit.la +nodist__audit_la_SOURCES = audit_wrap.c +audit.py audit_wrap.c: ${srcdir}/../src/auditswig.i + swig -o audit_wrap.c ${SWIG_FLAGS} ${SWIG_INCLUDES} ${srcdir}/../src/auditswig.i + +CLEANFILES = audit.py* audit_wrap.c *~ + diff --git a/framework/src/audit/bindings/swig/src/Makefile.am b/framework/src/audit/bindings/swig/src/Makefile.am new file mode 100644 index 00000000..590052aa --- /dev/null +++ b/framework/src/audit/bindings/swig/src/Makefile.am @@ -0,0 +1,26 @@ +# Makefile.am -- +# Copyright 2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +EXTRA_DIST = auditswig.i +SWIG_SOUIRCES = auditswig.i +CONFIG_CLEAN_FILES = *.loT *.rej *.orig + diff --git a/framework/src/audit/bindings/swig/src/auditswig.i b/framework/src/audit/bindings/swig/src/auditswig.i new file mode 100644 index 00000000..9364ac4b --- /dev/null +++ b/framework/src/audit/bindings/swig/src/auditswig.i @@ -0,0 +1,46 @@ +/* Author: Dan Walsh + * + * Copyright (C) 2005,2006,2009 Red Hat + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +%module audit +%{ + #include "../lib/libaudit.h" +%} + +#if defined(SWIGPYTHON) +%except(python) { + $action + if (result < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } +} +#endif + +%define __signed__ +signed +%enddef +#define __attribute(X) /*nothing*/ +typedef unsigned __u32; +typedef unsigned uid_t; +%include "/usr/include/linux/audit.h" +#define __extension__ /*nothing*/ +%include "/usr/include/stdint.h" +%include "../lib/libaudit.h" + diff --git a/framework/src/audit/compile b/framework/src/audit/compile new file mode 100755 index 00000000..531136b0 --- /dev/null +++ b/framework/src/audit/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/framework/src/audit/config.guess b/framework/src/audit/config.guess new file mode 100755 index 00000000..916300ba --- /dev/null +++ b/framework/src/audit/config.guess @@ -0,0 +1,1557 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2013 Free Software Foundation, Inc. + +timestamp='2013-06-10' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches with a ChangeLog entry to config-patches@gnu.org. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2013 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + or1k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/framework/src/audit/config.sub b/framework/src/audit/config.sub new file mode 100755 index 00000000..058edb7e --- /dev/null +++ b/framework/src/audit/config.sub @@ -0,0 +1,1788 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2013 Free Software Foundation, Inc. + +timestamp='2013-04-24' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches with a ChangeLog entry to config-patches@gnu.org. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2013 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 \ + | or1k | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=arm-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i386-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-* | ppc64p7-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or1k-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/framework/src/audit/configure.ac b/framework/src/audit/configure.ac new file mode 100644 index 00000000..1f48cb41 --- /dev/null +++ b/framework/src/audit/configure.ac @@ -0,0 +1,398 @@ +dnl +define([AC_INIT_NOTICE], +[### Generated automatically using autoconf version] AC_ACVERSION [ +### Copyright 2005-15 Steve Grubb +### +### Permission is hereby granted, free of charge, to any person obtaining a +### copy of this software and associated documentation files (the "Software"), +### to deal in the Software without restriction, including without limitation +### the rights to use, copy, modify, merge, publish, distribute, sublicense, +### and/or sell copies of the Software, and to permit persons to whom the +### Software is furnished to do so, subject to the following conditions: +### +### The above copyright notice and this permission notice shall be included +### in all copies or substantial portions of the Software. +### +### THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +### IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +### FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +### THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +### OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +### ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +### OTHER DEALINGS IN THE SOFTWARE. +### +### For usage, run `./configure --help' +### For more detailed information on installation, read the file `INSTALL'. +### +### If configuration succeeds, status is in the file `config.status'. +### A log of configuration tests is in `config.log'. +]) + +AC_REVISION($Revision: 1.3 $)dnl +AC_INIT(audit,2.4.4) +AC_PREREQ(2.12)dnl +AM_CONFIG_HEADER(config.h) + +echo Configuring auditd $VERSION + +AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE +AM_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +OLDLIBS="$LIBS" +m4_include([src/libev/libev.m4]) +libev_LIBS="$LIBS" +LIBS="$OLDLIBS" + +echo . +echo Checking for programs + +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_AWK +AX_PROG_CC_FOR_BUILD + +echo . +echo Checking for header files +AC_HEADER_STDC +AC_HEADER_TIME + +AC_C_CONST +AC_C_INLINE +AC_CHECK_SIZEOF([unsigned int]) +AC_CHECK_SIZEOF([unsigned long]) +AM_PROG_CC_C_O +AC_CHECK_DECLS([AUDIT_FEATURE_VERSION], [], [], [[#include ]]) +AC_CHECK_DECLS([AUDIT_VERSION_BACKLOG_WAIT_TIME], [], [], [[#include ]]) +AC_CHECK_DECLS([ADDR_NO_RANDOMIZE],,, [#include ]) +AC_CHECK_FUNCS([posix_fallocate]) + +ALLWARNS="" +ALLDEBUG="-g" +OPT="-O" +if test x"$GCC" = x"yes"; then + OPT="-O2 -pipe" + case "$target" in + *linux*) + ALLWARNS="-W -Wall -Wundef -Wpointer-arith -Wcast-align \ +-Wwrite-strings -Waggregate-return -Wstrict-prototypes \ +-Wmissing-prototypes -Wmissing-declarations -Wredundant-decls \ +-Wnested-externs -Winline -Wfloat-equal -Wchar-subscripts" + ;; + esac +fi + +AC_MSG_CHECKING(whether to create python bindings) +AC_ARG_WITH(python, +AS_HELP_STRING([--with-python],[enable building python bindings]), +use_python=$withval, +use_python=auto) +if test x$use_python = xno ; then + python_found="no" + AC_MSG_RESULT(no) +else +AC_MSG_RESULT(testing) +AM_PATH_PYTHON +PYINCLUDEDIR=`python${am_cv_python_version} -c "from distutils import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'))"` +if test -f ${PYINCLUDEDIR}/Python.h ; then + python_found="yes" + AC_SUBST(PYINCLUDEDIR) + pybind_dir="python" + AC_SUBST(pybind_dir) + AC_MSG_NOTICE(Python bindings will be built) +else + python_found="no" + if test x$use_python = xyes ; then + AC_MSG_ERROR([Python explicitly requested and python headers were not found]) + else + AC_MSG_WARN("Python headers not found - python bindings will not be made") + fi +fi +fi +AM_CONDITIONAL(HAVE_PYTHON, test ${python_found} = "yes") + +AC_MSG_CHECKING(whether to create python3 bindings) +AC_ARG_WITH(python3, +AS_HELP_STRING([--with-python3],[enable building python3 bindings]), +use_python3=$withval, +use_python3=auto) +if test x$use_python3 = xno ; then + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(investigating) + AC_PATH_PROG([use_python3], [python3-config], [no]) + if test ${use_python3} = no ; then + if test ${withval} = yes ; then + echo "Python3 bindings were selected but python3-config was not found." + echo "Please ensure that it's installed or pass --without-python3 to ./configure" + exit 1 + fi + echo "Python3 bindings will NOT be built" + else + echo "Python3 bindings WILL be built" + use_python3=yes + AC_PATH_PROG([PYTHON3], [python3], [no]) + if test "$PYTHON3" == "no" ; then + echo "The python3 program was not found in the search path. Please ensure" + echo "that it is installed and its directory is included in the search path or" + echo "pass --without-python3 to ./configure." + exit 1 + fi + PYTHON3_CFLAGS=`python3-config --cflags 2> /dev/null` + PYTHON3_LIBS=`python3-config --libs 2> /dev/null` + PYTHON3_INCLUDES=`python3-config --includes 2> /dev/null` + AC_SUBST([PYTHON3_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON3_EXEC_PREFIX], ['${exec_prefix}']) + PYTHON3_DIR=`$PYTHON3 -c "import distutils.sysconfig; \ + print(distutils.sysconfig.get_python_lib(0,0,prefix='$PYTHON3_PREFIX'))"` + PYTHON3_EXECDIR=`$PYTHON3 -c "import distutils.sysconfig; \ + print(distutils.sysconfig.get_python_lib(1,0,prefix='$PYTHON3_EXEC_PREFIX'))"` + AC_SUBST(PYTHON3_CFLAGS) + AC_SUBST(PYTHON3_LIBS) + AC_SUBST(PYTHON3_INCLUDES) + AC_SUBST(python3dir, $PYTHON3_DIR) + AC_SUBST(py3execdir, $PYTHON3_EXECDIR) + fi +fi +AM_CONDITIONAL(USE_PYTHON3, test ${use_python3} = "yes") + +AC_MSG_CHECKING(whether to create Go language bindings) +AC_ARG_WITH(golang, +AS_HELP_STRING([--with-golang],[enable building golang bindings]), +use_golang=$withval, +use_golang=auto) +if test x$use_golang = xno ; then + golang_found="no" + AC_MSG_RESULT(no) +else + AC_MSG_RESULT(testing) + AC_CHECK_PROG([GOLANG],[go],[go],[no]) + AS_IF([test "x$GOLANG" != "xno"],[ + AC_MSG_NOTICE(Go bindings will be built) + golang_found="yes" + + # Substitute some golang environment. + GOROOT=`$GOLANG env GOROOT` + AC_SUBST([GOROOT]) + gobind_dir="golang" + AC_SUBST([gobind_dir]) + ], [ + if test x$use_golang = xyes ; then + AC_MSG_ERROR([Go language explicitly requested and program not found]) + else + AC_MSG_WARN("Go not found - go bindings will not be made") + fi + ]) +fi +AM_CONDITIONAL(HAVE_GOLANG, test ${golang_found} = "yes") + +#auditd listener +AC_MSG_CHECKING(whether to include auditd network listener support) +AC_ARG_ENABLE(listener, + [AS_HELP_STRING([--disable-listener], + [Disable auditd network listener support])], + enable_listener=$enableval, + enable_listener=yes) +if test "x$enable_listener" != "xno"; then + AC_DEFINE(USE_LISTENER, 1, + [Define if you want to use the auditd network listener.]) +fi +AM_CONDITIONAL(ENABLE_LISTENER, test "x$enable_listener" != "xno") +AC_MSG_RESULT($enable_listener) + +#audisp zos-remote plugin +AC_MSG_CHECKING(whether to include audisp ZOS remote plugin) +AC_ARG_ENABLE(zos-remote, + [AS_HELP_STRING([--disable-zos-remote], + [Disable audisp ZOS remote plugin])], + enable_zos_remote=$enableval, + enable_zos_remote=yes) +AM_CONDITIONAL(ENABLE_ZOS_REMOTE, test "x$enable_zos_remote" != "xno") +AC_MSG_RESULT($enable_zos_remote) + +#gssapi +AC_ARG_ENABLE(gssapi_krb5, + [AS_HELP_STRING([--enable-gssapi-krb5],[Enable GSSAPI Kerberos 5 support @<:@default=no@:>@])], + [case "${enableval}" in + yes) want_gssapi_krb5="yes" ;; + no) want_gssapi_krb5="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-gssapi-krb5) ;; + esac], + [want_gssapi_krb5="no"] +) +if test $want_gssapi_krb5 = yes; then + AC_CHECK_LIB(gssapi_krb5, gss_acquire_cred, [ + AC_CHECK_HEADER(gssapi/gssapi.h, [ + AC_DEFINE(USE_GSSAPI,, + Define if you want to use GSSAPI) + gss_libs="-lgssapi_krb5 -lkrb5" + AC_SUBST(gss_libs) + ]) + ]) +fi +AM_CONDITIONAL(ENABLE_GSSAPI, test x$want_gssapi_krb5 = xyes) + +#systemd +AC_ARG_ENABLE(systemd, + [AS_HELP_STRING([--enable-systemd],[Enable systemd init scripts @<:@default=no@:>@])], + [case "${enableval}" in + yes) want_systemd="yes" ;; + no) want_systemd="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-systemd) ;; + esac], + [want_systemd="no"] +) +AM_CONDITIONAL(ENABLE_SYSTEMD, test x$want_systemd = xyes) + +ALLDEBUG="-g" +AC_ARG_WITH(debug, +[ --with-debug turn on debugging [[default=no]]], +[ +if test "x${withval}" = xyes; then + DEBUG="$ALLDEBUG" + OPT="-O" + AM_CONDITIONAL(DEBUG, true) +else + DEBUG="-DNDEBUG" + AM_CONDITIONAL(DEBUG, false) +fi +], +[ DEBUG="-DNDEBUG"; AM_CONDITIONAL(DEBUG, false) ]) + +AC_ARG_WITH(warn, +[ --with-warn turn on warnings [[default=yes]]], +[ +if test "x${withval}" = xyes; then + WARNS="$ALLWARNS" +else + WARNS="" +fi +],WARNS="$ALLWARNS") + +AC_MSG_CHECKING(whether to include alpha processor support) +AC_ARG_WITH(alpha, +AS_HELP_STRING([--with-alpha],[enable Alpha processor support]), +use_alpha=$withval, +use_alpha=no) +if test x$use_alpha != xno ; then + AC_DEFINE(WITH_ALPHA,1,[Define if you want to enable Alpha processor support.]) +fi +AM_CONDITIONAL(USE_ALPHA, test x$use_alpha = xyes) +AC_MSG_RESULT($use_alpha) + +AC_MSG_CHECKING(whether to include arm eabi processor support) +AC_ARG_WITH(arm, +AS_HELP_STRING([--with-arm],[enable Arm eabi processor support]), +use_arm=$withval, +use_arm=no) +if test x$use_arm != xno ; then + AC_DEFINE(WITH_ARM,1,[Define if you want to enable Arm eabi processor support.]) +fi +AM_CONDITIONAL(USE_ARM, test x$use_arm = xyes) +AC_MSG_RESULT($use_arm) + +AC_MSG_CHECKING(whether to include aarch64 processor support) +AC_ARG_WITH(aarch64, +AS_HELP_STRING([--with-aarch64],[enable Aarch64 processor support]), +use_aarch64=$withval, +use_aarch64=no) +if test x$use_aarch64 != xno ; then + AC_DEFINE(WITH_AARCH64,1,[Define if you want to enable Aarch64 processor support.]) +fi +AM_CONDITIONAL(USE_AARCH64, test x$use_aarch64 = xyes) +AC_MSG_RESULT($use_aarch64) + +AC_MSG_CHECKING(whether to use apparmor) +AC_ARG_WITH(apparmor, +AS_HELP_STRING([--with-apparmor],[enable AppArmor events]), +use_apparmor=$withval, +use_apparmor=no) +if test x$use_apparmor != xno ; then + AC_DEFINE(WITH_APPARMOR,1,[Define if you want to enable AppArmor events.]) +fi +AC_MSG_RESULT($use_apparmor) + +AC_MSG_CHECKING(whether to use prelude) +AC_ARG_WITH(prelude, +AS_HELP_STRING([--with-prelude],[enable prelude IDS support]), +use_prelude=$withval, +use_prelude=no) +AC_MSG_RESULT($use_prelude) +if test x$use_prelude = xno ; then + have_prelude=no; +else + AC_CHECK_LIB(prelude, prelude_init, + have_prelude=yes, have_prelude=no) + if test x$have_prelude = xno ; then + AC_MSG_ERROR([Prelude explicitly required and prelude library not found]) + else + LIBPRELUDE_CFLAGS=`libprelude-config --pthread-cflags 2>/dev/null` + LIBPRELUDE_LDFLAGS=`libprelude-config --ldflags 2>/dev/null` + AC_MSG_RESULT(yes) + fi +fi +AM_CONDITIONAL(HAVE_PRELUDE, test x$have_prelude = xyes) + +AC_MSG_CHECKING(whether to use libwrap) +AC_ARG_WITH(libwrap, +[ --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support.], +[ case "$withval" in + no) + AC_MSG_RESULT(no) + ;; + yes) + AC_MSG_RESULT(yes) + AC_CHECK_HEADER(tcpd.h, [], + AC_MSG_ERROR([Could not find libwrap headers]),) + AC_CHECK_LIB(wrap, request_init, [ LIBWRAP_LIBS="-lwrap" ]) + AC_CHECK_LIB(nsl, yp_get_default_domain, [ + LIBWRAP_LIBS="$LIBWRAP_LIBS -lnsl" ]) + ;; + *) + AC_MSG_RESULT(yes) + if test -d "$withval"; then + LIBWRAP_LIBS="-L$withval -lwrap" + else + LIBWRAP_LIBS="$withval" + fi + AC_CHECK_HEADER(tcpd.h, [], + AC_MSG_ERROR([Could not find libwrap headers])) + AC_CHECK_LIB(wrap, request_init, []) + AC_CHECK_LIB(nsl, yp_get_default_domain, [ + LIBWRAP_LIBS="$LIBWRAP_LIBS -lnsl" ]) + OLDLIBS="$LIBS" + LIBS="$LIBWRAP_LIBS $LIBS" + AC_TRY_LINK([ int allow_severity; int deny_severity; ], + [ hosts_access(); ], [], + [ AC_MSG_ERROR(Could not find the $withval library. You must first install tcp_wrappers.) ]) + LIBS="$OLDLIBS" + ;; + esac ], + AC_MSG_RESULT(no) +) +if test x"$LIBWRAP_LIBS" != "x"; then + AC_DEFINE_UNQUOTED(HAVE_LIBWRAP, [], Define if tcp_wrappers support is enabled ) +fi + +# See if we want to support lower capabilities for plugins +LIBCAP_NG_PATH + +AC_SUBST(DEBUG) +AC_SUBST(LIBWRAP_LIBS) +#AC_SUBST(libev_LIBS) +AC_SUBST(LIBPRELUDE_CFLAGS) +AC_SUBST(LIBPRELUDE_LDFLAGS) + +AC_OUTPUT(Makefile lib/Makefile lib/audit.pc lib/test/Makefile auparse/Makefile auparse/test/Makefile auparse/auparse.pc src/Makefile src/mt/Makefile src/libev/Makefile src/test/Makefile docs/Makefile init.d/Makefile audisp/Makefile audisp/plugins/Makefile audisp/plugins/builtins/Makefile audisp/plugins/prelude/Makefile audisp/plugins/remote/Makefile audisp/plugins/zos-remote/Makefile bindings/Makefile bindings/python/Makefile bindings/python/python2/Makefile bindings/python/python3/Makefile bindings/golang/Makefile bindings/swig/Makefile bindings/swig/src/Makefile bindings/swig/python/Makefile bindings/swig/python3/Makefile tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile tools/ausyscall/Makefile tools/auvirt/Makefile) + +echo . +echo " + + Auditd Version: $VERSION + Target: $target + Installation prefix: $prefix + Compiler: $CC + Compiler flags: +`echo $CFLAGS | fmt -w 50 | sed 's,^, ,'` +" diff --git a/framework/src/audit/contrib/avc_snap b/framework/src/audit/contrib/avc_snap new file mode 100755 index 00000000..f4acba7d --- /dev/null +++ b/framework/src/audit/contrib/avc_snap @@ -0,0 +1,90 @@ +#! /usr/bin/env python +import os, string, select, struct, syslog +import audit, avc, traceback +import AuditMsg +from setroubleshoot.signature import * +from setroubleshoot.util import LoadPlugins + +class avc_snap: + def __init__(self): + self.audit_list = [] + self.cur_sig = "" + self.plugins = LoadPlugins() + syslog.syslog( "Number of Plugins = %d" % len(self.plugins)) + + def is_avc(self): + for i in self.audit_list: + if i[0] == audit.AUDIT_AVC: + return True + return False + + def out(self): + if self.is_avc(): + rules=avc.SERules() + l=[] + for ( type, data_list ) in self.audit_list: + l += data_list + + if "granted" in l: + self.audit_list = [] + return + + rules.translate(l) + myavc = AVC(rules.AVCS[0]) + for plugin in self.plugins: + try: + if plugin.analyze(myavc): + plugin.report() + break; + + except TypeError, e: + syslog.syslog("Type exception %s: %s " % ( plugin.analysisID, e.args)) + except: + syslog.syslog("Plugin Exception %s " % plugin.analysisID) + + self.audit_list = [] + + def process(self, type, data): + data_list=data.split() + new_sig=data_list[0] + + if len(self.audit_list) > 0 and new_sig != self.cur_sig: + self.out() + self.cur_sig = new_sig + + self.audit_list.append((type, data_list[1:])) + + def run(self): + while 1: + input,output, err = select.select([0],[], [], 5) + try: + if 0 in input: + msg = AuditMsg.AuditMsg() + if not msg.read_from_fd(0): + syslog.syslog("Connection closing") + return + self.process(msg.get_type(), msg.get_body()) + else: + self.out() + + except struct.error, e: + syslog.syslog("struct exception %s " % e.args) + return + except TypeError, e: + syslog.syslog("Type exception %s " % e.args) + +try: + syslog.openlog("avc_snap") + snap=avc_snap() + snap.run() + +except IOError,e: + syslog.syslog("IOError exception %s" % e.args) + +except Exception, e: + syslog.syslog("Unexpected exception %s " % e.args) + syslog.syslog(traceback.format_exc()) + +except: + syslog.syslog("Caught Exception") + syslog.syslog(traceback.format_exc()) diff --git a/framework/src/audit/contrib/capp.rules b/framework/src/audit/contrib/capp.rules new file mode 100644 index 00000000..5e38274f --- /dev/null +++ b/framework/src/audit/contrib/capp.rules @@ -0,0 +1,302 @@ +## +## This file contains a sample audit configuration. Combined with the +## system events that are audited by default, this set of rules causes +## audit to generate records for the auditable events specified by the +## Controlled Access Protection Profile (CAPP). +## +## It should be noted that this set of rules identifies directories by +## leaving a / at the end of the path. +## +## For audit 2.0.6 and higher +## + +## Remove any existing rules +-D + +## Increase buffer size to handle the increased number of messages. +## Feel free to increase this if the machine panic's +-b 8192 + +## Set failure mode to panic +-f 2 + +## +## FAU_SAR.1, FAU_SAR.2, FMT_MTD.1 +## successful and unsuccessful attempts to read information from the +## audit records; all modifications to the audit trail +## +-w /var/log/audit/ -k LOG_audit + +## +## FAU_SEL.1, FMT_MTD.1 +## modifications to audit configuration that occur while the audit +## collection functions are operating; all modications to the set of +## audited events +## +-w /etc/audit/ -p wa -k CFG_audit +-w /etc/sysconfig/auditd -p wa -k CFG_auditd.conf +-w /etc/libaudit.conf -p wa -k CFG_libaudit.conf +-w /etc/audisp/ -p wa -k CFG_audisp + +## +## FDP_ACF.1, FMT_MSA.1, FMT_MTD.1, FMT_REV.1 +## all requests to perform an operation on an object covered by the +## SFP; all modifications of the values of security attributes; +## modifications to TSF data; attempts to revoke security attributes +## + +## Objects covered by the Security Functional Policy (SFP) are: +## -File system objects (files, directories, special files, extended attributes) +## -IPC objects (SYSV shared memory, message queues, and semaphores) + +## Operations on file system objects - by default, only monitor +## files and directories covered by filesystem watches. + +## Changes in ownership and permissions +#-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat +#-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat +#-a always,exit -F arch=b32 -S chown,fchown,fchownat,lchown +#-a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown +## Enable *32 rules if you are running on i386 or s390 +## Do not use for x86_64, ia64, ppc, ppc64, or s390x +#-a always,exit -F arch=b32 -S fchown32,chown32,lchown32 + +## File content modification. Permissions are checked at open time, +## monitoring individual read/write calls is not useful. +#-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate,fallocate +#-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate,fallocate +## Enable *64 rules if you are running on i386, ppc, ppc64, s390 +## Do not use for x86_64, ia64, or s390x +#-a always,exit -F arch=b32 -S truncate64,ftruncate64 + +## directory operations +#-a always,exit -F arch=b32 -S mkdir,mkdirat,rmdir +#-a always,exit -F arch=b64 -S mkdir,mkdirat,rmdir + +## moving, removing, and linking +#-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat +#-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat +#-a always,exit -F arch=b32 -S link,linkat,symlink,symlinkat +#-a always,exit -F arch=b64 -S link,linkat,symlink,symlinkat + +## Extended attribute operations +## Enable if you are interested in these events +#-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr +#-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr + +## special files +-a always,exit -F arch=b32 -S mknod,mknodat +-a always,exit -F arch=b64 -S mknod,mknodat + +## Other file system operations +## Enable if i386 +-a always,exit -F arch=b32 -S mount,umount,umount2 +## Enable if ppc, s390, or s390x +#-a always,exit -F arch=b32 -S mount,umount,umount2 +#-a always,exit -F arch=b64 -S mount,umount,umount2 +## Enable if ia64 +#-a always,exit -F arch=b64 -S mount,umount +## Enable if x86_64 +#-a always,exit -F arch=b64 -S mount,umount2 +#-a always,exit -F arch=b32 -S mount,umount,umount2 + +## IPC SYSV message queues +## Enable if you are interested in these events (x86,ppc,ppc64,s390,s390x) +## msgctl +#-a always,exit -S ipc -F a0=14 +## msgget +#-a always,exit -S ipc -F a0=13 +## Enable if you are interested in these events (x86_64,ia64) +#-a always,exit -S msgctl +#-a always,exit -S msgget + +## IPC SYSV semaphores +## Enable if you are interested in these events (x86,ppc,ppc64,s390,s390x) +## semctl +#-a always,exit -S ipc -F a0=3 +## semget +#-a always,exit -S ipc -F a0=2 +## semop +#-a always,exit -S ipc -F a0=1 +## semtimedop +#-a always,exit -S ipc -F a0=4 +## Enable if you are interested in these events (x86_64, ia64) +#-a always,exit -S semctl +#-a always,exit -S semget +#-a always,exit -S semop +#-a always,exit -S semtimedop + +## IPC SYSV shared memory +## Enable if you are interested in these events (x86,ppc,ppc64,s390,s390x) +## shmctl +#-a always,exit -S ipc -F a0=24 +## shmget +#-a always,exit -S ipc -F a0=23 +## Enable if you are interested in these events (x86_64, ia64) +#-a always,exit -S shmctl +#-a always,exit -S shmget + +## +## FIA_USB.1 +## success and failure of binding user security attributes to a subject +## +## Enable if you are interested in these events +## +#-a always,exit -F arch=b32 -S clone +#-a always,exit -F arch=b64 -S clone +#-a always,exit -F arch=b32 -S fork,vfork +#-a always,exit -F arch=b64 -S fork,vfork +## For ia64 architecture, disable fork and vfork rules above, and +## enable the following: +#-a always,exit -S clone2 + +## +## FMT_MSA.3 +## modifications of the default setting of permissive or restrictive +## rules, all modifications of the initial value of security attributes +## +## Enable if you are interested in these events +## +#-a always,exit -F arch=b32 -S umask +#-a always,exit -F arch=b64 -S umask + +## +## FPT_STM.1 +## changes to the time +## +-a always,exit -F arch=b32 -S adjtimex,settimeofday -S stime +-a always,exit -F arch=b64 -S adjtimex,settimeofday +-a always,exit -F arch=b32 -S clock_settime -F a0=0 +-a always,exit -F arch=b64 -S clock_settime -F a0=0 +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=time-change + +## +## FTP_ITC.1 +## set-up of trusted channel +## +-w /usr/sbin/stunnel -p x + +## +## Security Databases +## + +## cron configuration & scheduled jobs +-w /etc/cron.allow -p wa -k CFG_cron.allow +-w /etc/cron.deny -p wa -k CFG_cron.deny +-w /etc/cron.d/ -p wa -k CFG_cron.d +-w /etc/cron.daily/ -p wa -k CFG_cron.daily +-w /etc/cron.hourly/ -p wa -k CFG_cron.hourly +-w /etc/cron.monthly/ -p wa -k CFG_cron.monthly +-w /etc/cron.weekly/ -p wa -k CFG_cron.weekly +-w /etc/crontab -p wa -k CFG_crontab +-w /var/spool/cron/root -k CFG_crontab_root + +## user, group, password databases +-w /etc/group -p wa -k CFG_group +-w /etc/passwd -p wa -k CFG_passwd +-w /etc/gshadow -k CFG_gshadow +-w /etc/shadow -k CFG_shadow +-w /etc/security/opasswd -k CFG_opasswd + +## login configuration and information +-w /etc/login.defs -p wa -k CFG_login.defs +-w /etc/securetty -p wa -k CFG_securetty +-w /var/run/faillock/ -p wa -k LOG_faillock +-w /var/log/lastlog -p wa -k LOG_lastlog +-w /var/log/tallylog -p wa -k LOG_tallylog + +## network configuration +-w /etc/hosts -p wa -k CFG_hosts +-w /etc/sysconfig/network-scripts/ -p wa -k CFG_network + +## system startup scripts +-w /etc/sysconfig/init -p wa -k CFG_init +-w /etc/init/ -p wa -k CFG_init +-w /etc/inittab -p wa -k CFG_inittab +-w /etc/rc.d/init.d/ -p wa -k CFG_initscripts + +## library search paths +-w /etc/ld.so.conf -p wa -k CFG_ld.so.conf + +## local time zone +-w /etc/localtime -p wa -k CFG_localtime + +## kernel parameters +-w /etc/sysctl.conf -p wa -k CFG_sysctl.conf + +## modprobe configuration +-w /etc/modprobe.d/ -p wa -k CFG_modprobe + +## pam configuration +-w /etc/pam.d/ -p wa -k CFG_pam +-w /etc/security/access.conf -p wa -k CFG_pam +-w /etc/security/limits.conf -p wa -k CFG_pam +-w /etc/security/pam_env.conf -p wa -k CFG_pam +-w /etc/security/namespace.conf -p wa -k CFG_pam +-w /etc/security/namespace.d/ -p wa -k CFG_pam +-w /etc/security/namespace.init -p wa -k CFG_pam +-w /etc/security/sepermit.conf -p wa -k CFG_pam +-w /etc/security/time.conf -p wa -k CFG_pam + +## postfix configuration +-w /etc/aliases -p wa -k CFG_aliases +-w /etc/postfix/ -p wa -k CFG_postfix + +## screen configuration +-w /etc/screenrc -p wa -k CFG_screen + +## ssh configuration +-w /etc/ssh/sshd_config -k CFG_sshd_config + +## stunnel configuration +-w /etc/stunnel/stunnel.conf -k CFG_stunnel.conf +-w /etc/stunnel/stunnel.pem -k CFG_stunnel.pem + +## sudo configuration +-w /etc/sudoers -k CFG_sudoers +-w /etc/sudoers.d/ -k CFG_sudoers + +## Not specifically required by CAPP; but common sense items +-a always,exit -F arch=b32 -S sethostname -S setdomainname +-a always,exit -F arch=b64 -S sethostname -S setdomainname +-w /etc/issue -p wa -k CFG_issue +-w /etc/issue.net -p wa -k CFG_issue.net + +## Optional - could indicate someone trying to do something bad or +## just debugging +#-a always,exit -F arch=b32 -S ptrace -F key=tracing +#-a always,exit -F arch=b64 -S ptrace -F key=tracing +#-a always,exit -F arch=b32 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x6 -F key=register-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x6 -F key=register-injection + +## Optional - might want to watch module insertion +#-w /sbin/insmod -p x -k modules +#-w /sbin/rmmod -p x -k modules +#-w /sbin/modprobe -p x -k modules +#-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b32 -S delete_module -F key=module-unload +#-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +## Optional - admin may be abusing power by looking in user's home dir +#-a always,exit -F dir=/home -F uid=0 -F auid>=1000 -F auid!=4294967295 -C auid!=obj_uid -F key=power-abuse + +## Optional - log container creation +#-a always,exit -F arch=b32 -S clone -F a0&2080505856 -F key=container-create +#-a always,exit -F arch=b64 -S clone -F a0&2080505856 -F key=container-create + +## Optional - watch for containers that may change their configuration +#-a always,exit -F arch=b32 -S unshare,setns -F key=container-config +#-a always,exit -F arch=b64 -S unshare,setns -F key=container-config + +## Put your own watches after this point +# -w /your-file -p rwxa -k mykey + +## Make the configuration immutable +#-e 2 diff --git a/framework/src/audit/contrib/lspp.rules b/framework/src/audit/contrib/lspp.rules new file mode 100644 index 00000000..e0919bd2 --- /dev/null +++ b/framework/src/audit/contrib/lspp.rules @@ -0,0 +1,343 @@ +## +## This file contains a sample audit configuration. Combined with the +## system events that are audited by default, this set of rules causes +## audit to generate records for the auditable events specified by the +## Labeled Security Protection Profile (LSPP). +## +## It should be noted that this set of rules identifies directories by +## leaving a / at the end of the path. +## +## For audit 2.0.6 and higher +## + +## Remove any existing rules +-D + +## Increase buffer size to handle the increased number of messages. +## Feel free to increase this if the machine panic's +-b 8192 + +## Set failure mode to panic +-f 2 + +## +## FAU_SAR.1, FAU_SAR.2, FMT_MTD.1 +## successful and unsuccessful attempts to read information from the +## audit records; all modifications to the audit trail +## +-w /var/log/audit/ -k LOG_audit + +## +## FAU_SEL.1, FMT_MTD.1 +## modifications to audit configuration that occur while the audit +## collection functions are operating; all modications to the set of +## audited events +## +-w /etc/audit/ -p wa -k CFG_audit +-w /etc/sysconfig/auditd -p wa -k CFG_auditd.conf +-w /etc/libaudit.conf -p wa -k CFG_libaudit.conf +-w /etc/audisp/ -p wa -k CFG_audisp + +## +## FDP_ACF.1, FMT_MSA.1, FMT_MTD.1, FMT_REV.1, FDP_ETC.1, FDP_ITC.2 +## all requests to perform an operation on an object covered by the +## SFP; all modifications of the values of security attributes; +## modifications to TSF data; attempts to revoke security attributes; +## all attempts to export information; all attempts to import user +## data, including any security attributes + +## Objects covered by the Security Functional Policy (SFP) are: +## -File system objects (files, directories, special files, extended attributes) +## -IPC objects (SYSV shared memory, message queues, and semaphores) + +## Operations on file system objects - by default, only monitor +## files and directories covered by filesystem watches. + +## Changes in ownership and permissions +#-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat +#-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat +#-a always,exit -F arch=b32 -S chown,fchown,fchownat,lchown +#-a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown +## Enable *32 rules if you are running on i386 or s390 +## Do not use for x86_64, ia64, ppc, ppc64, or s390x +#-a always,exit -F arch=b32 -S fchown32,chown32,lchown32 + +## File content modification. Permissions are checked at open time, +## monitoring individual read/write calls is not useful. +#-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate,fallocate +#-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate,fallocate +## Enable *64 rules if you are running on i386, ppc, ppc64, s390 +## Do not use for x86_64, ia64, or s390x +#-a always,exit -F arch=b32 -S truncate64,ftruncate64 + +## directory operations +#-a always,exit -F arch=b32 -S mkdir,mkdirat,rmdir +#-a always,exit -F arch=b64 -S mkdir,mkdirat,rmdir + +## moving, removing, and linking +#-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat +#-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat +#-a always,exit -F arch=b32 -S link,linkat,symlink,symlinkat +#-a always,exit -F arch=b64 -S link,linkat,symlink,symlinkat + +## Extended attribute operations +## Enable if you are interested in these events +-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr +-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr + +## special files +-a always,exit -F arch=b32 -S mknod,mknodat +-a always,exit -F arch=b64 -S mknod,mknodat + +## Other file system operations +## Enable if i386 +-a always,exit -F arch=b32 -S mount,umount,umount2 +## Enable if ppc, s390, or s390x +#-a always,exit -F arch=b32 -S mount,umount,umount2 +#-a always,exit -F arch=b64 -S mount,umount,umount2 +## Enable if ia64 +#-a always,exit -F arch=b64 -S mount,umount +## Enable if x86_64 +#-a always,exit -F arch=b64 -S mount,umount2 +#-a always,exit -F arch=b32 -S mount,umount,umount2 + +## IPC SYSV message queues +## Enable if you are interested in these events (x86,ppc,ppc64,s390,s390x) +## msgctl +#-a always,exit -S ipc -F a0=14 +## msgget +#-a always,exit -S ipc -F a0=13 +## Enable if you are interested in these events (x86_64,ia64) +#-a always,exit -S msgctl +#-a always,exit -S msgget + +## IPC SYSV semaphores +## Enable if you are interested in these events (x86,ppc,ppc64,s390,s390x) +## semctl +#-a always,exit -S ipc -F a0=0x3 +## semget +#-a always,exit -S ipc -F a0=0x2 +## semop +#-a always,exit -S ipc -F a0=0x1 +## semtimedop +#-a always,exit -S ipc -F a0=0x4 +## Enable if you are interested in these events (x86_64, ia64) +#-a always,exit -S semctl +#-a always,exit -S semget +#-a always,exit -S semop +#-a always,exit -S semtimedop + +## IPC SYSV shared memory +## Enable if you are interested in these events (x86,ppc,ppc64,s390,s390x) +## shmctl +#-a always,exit -S ipc -F a0=24 +## shmget +#-a always,exit -S ipc -F a0=23 +## Enable if you are interested in these events (x86_64, ia64) +#-a always,exit -S shmctl +#-a always,exit -S shmget + +## +## FIA_USB.1 +## success and failure of binding user security attributes to a subject +## +## Enable if you are interested in these events +## +#-a always,exit -F arch=b32 -S clone +#-a always,exit -F arch=b64 -S clone +#-a always,exit -F arch=b32 -S fork,vfork +#-a always,exit -F arch=b64 -S fork,vfork +## For ia64 architecture, disable fork and vfork rules above, and +## enable the following: +#-a always,exit -S clone2 + +## +## FDP_ETC.2 +## Export of Labeled User Data +## +## Printing +-w /etc/cups/ -p wa -k CFG_cups +-w /etc/init.d/cups -p wa -k CFG_initd_cups + +## +## FDP_ETC.2, FDP_ITC.2 +## Export/Import of Labeled User Data +## +## Networking +-w /etc/netlabel.rules -p wa -k CFG_netlabel.rules +-w /etc/ipsec.conf -p wa -k CFG_ipsec.conf +-w /etc/ipsec.d/ -p wa -k CFG_ipsec.conf +-w /etc/ipsec.secrets -p wa -k CFG_ipsec.secrets + +## +## FDP_IFC.1 +## Mandatory Access Control Policy +## +-w /etc/selinux/config -p wa -k CFG_selinux_config +-w /etc/selinux/mls/ -p wa -k CFG_MAC_policy +-w /usr/share/selinux/mls/ -p wa -k CFG_MAC_policy +-w /etc/selinux/semanage.conf -p wa -k CFG_MAC_policy + +## +## FMT_MSA.3 +## modifications of the default setting of permissive or restrictive +## rules, all modifications of the initial value of security attributes +## +## Enable if you are interested in these events +## +#-a always,exit -F arch=b32 -S umask +#-a always,exit -F arch=b64 -S umask + +## +## FPT_STM.1 +## changes to the time +## +-a always,exit -F arch=b32 -S stime,adjtimex,settimeofday +-a always,exit -F arch=b64 -S adjtimex,settimeofday +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=time-change + +## +## FTP_ITC.1 +## set-up of trusted channel +## +-w /usr/sbin/stunnel -p x + +## +## FPT_TST.1 Self Test +## aide is used to verify integrity of data and executables +## +-w /etc/aide.conf -p wa -k CFG_aide.conf +-w /var/lib/aide/aide.db.gz -k CFG_aide.db +-w /var/lib/aide/aide.db.new.gz -k CFG_aide.db +-w /var/log/aide/ -p wa -k CFG_aide.log + +## +## Security Databases +## + +## cron configuration & scheduled jobs +-w /etc/cron.allow -p wa -k CFG_cron.allow +-w /etc/cron.deny -p wa -k CFG_cron.deny +-w /etc/cron.d/ -p wa -k CFG_cron.d +-w /etc/cron.daily/ -p wa -k CFG_cron.daily +-w /etc/cron.hourly/ -p wa -k CFG_cron.hourly +-w /etc/cron.monthly/ -p wa -k CFG_cron.monthly +-w /etc/cron.weekly/ -p wa -k CFG_cron.weekly +-w /etc/crontab -p wa -k CFG_crontab +-w /var/spool/cron/root -k CFG_crontab_root + +## user, group, password databases +-w /etc/group -p wa -k CFG_group +-w /etc/passwd -p wa -k CFG_passwd +-w /etc/gshadow -k CFG_gshadow +-w /etc/shadow -k CFG_shadow +-w /etc/security/opasswd -k CFG_opasswd + +## login configuration and information +-w /etc/login.defs -p wa -k CFG_login.defs +-w /etc/securetty -p wa -k CFG_securetty +-w /var/run/faillock/ -p wa -k LOG_faillock +-w /var/log/lastlog -p wa -k LOG_lastlog +-w /var/log/tallylog -p wa -k LOG_tallylog + +## network configuration +-w /etc/hosts -p wa -k CFG_hosts +-w /etc/sysconfig/network-scripts/ -p wa -k CFG_network + +## system startup scripts +-w /etc/sysconfig/init -p wa -k CFG_init +-w /etc/init/ -p wa -k CFG_init +-w /etc/inittab -p wa -k CFG_inittab +-w /etc/rc.d/init.d/ -p wa -k CFG_initscripts + +## library search paths +-w /etc/ld.so.conf -p wa -k CFG_ld.so.conf + +## local time zone +-w /etc/localtime -p wa -k CFG_localtime + +## kernel parameters +-w /etc/sysctl.conf -p wa -k CFG_sysctl.conf + +## modprobe configuration +-w /etc/modprobe.d/ -p wa -k CFG_modprobe + +## pam configuration +-w /etc/pam.d/ -p wa -k CFG_pam +-w /etc/security/access.conf -p wa -k CFG_pam +-w /etc/security/limits.conf -p wa -k CFG_pam +-w /etc/security/pam_env.conf -p wa -k CFG_pam +-w /etc/security/namespace.conf -p wa -k CFG_pam +-w /etc/security/namespace.d/ -p wa -k CFG_pam +-w /etc/security/namespace.init -p wa -k CFG_pam +-w /etc/security/sepermit.conf -p wa -k CFG_pam +-w /etc/security/time.conf -p wa -k CFG_pam + +## postfix configuration +-w /etc/aliases -p wa -k CFG_aliases +-w /etc/postfix/ -p wa -k CFG_postfix + +## screen configuration +-w /etc/screenrc -p wa -k CFG_screen + +## ssh configuration +-w /etc/ssh/sshd_config -k CFG_sshd_config + +## stunnel configuration +-w /etc/stunnel/stunnel.conf -k CFG_stunnel.conf +-w /etc/stunnel/stunnel.pem -k CFG_stunnel.pem + +## sudo configuration +-w /etc/sudoers -k CFG_sudoers +-w /etc/sudoers.d/ -k CFG_sudoers + +## xinetd configuration +-w /etc/xinetd.d/ -k CFG_xinetd.d +-w /etc/xinetd.conf -k CFG_xinetd.conf + +## Not specifically required by LSPP; but common sense items +-a always,exit -F arch=b32 -S sethostname,setdomainname +-a always,exit -F arch=b64 -S sethostname,setdomainname +-w /etc/issue -p wa -k CFG_issue +-w /etc/issue.net -p wa -k CFG_issue.net + +## Optional - could indicate someone trying to do something bad or +## just debugging +#-a always,exit -F arch=b32 -S ptrace -F key=tracing +#-a always,exit -F arch=b64 -S ptrace -F key=tracing +#-a always,exit -F arch=b32 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x6 -F key=register-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x6 -F key=register-injection + +## Optional - might want to watch module insertion +#-w /sbin/insmod -p x -k modules +#-w /sbin/rmmod -p x -k modules +#-w /sbin/modprobe -p x -k modules +#-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b32 -S delete_module -F key=module-unload +#-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +## Optional - admin may be abusing power by looking in user's home dir +#-a always,exit -F dir=/home -F uid=0 -F auid>=1000 -F auid!=4294967295 -C auid!=obj_uid -F key=power-abuse + +## Optional - log container creation +#-a always,exit -F arch=b32 -S clone -F a0&0x2080505856 -F key=container-create +#-a always,exit -F arch=b64 -S clone -F a0&0x2080505856 -F key=container-create + +## Optional - watch for containers that may change their configuration +#-a always,exit -F arch=b32 -S unshare,setns -F key=container-config +#-a always,exit -F arch=b64 -S unshare,setns -F key=container-config + +## Put your own watches after this point +# -w /your-file -p rwxa -k mykey + +## Make the configuration immutable +#-e 2 diff --git a/framework/src/audit/contrib/nispom.rules b/framework/src/audit/contrib/nispom.rules new file mode 100644 index 00000000..6bcca086 --- /dev/null +++ b/framework/src/audit/contrib/nispom.rules @@ -0,0 +1,148 @@ +## +## This file contains the a sample audit configuration intended to +## meet the NISPOM Chapter 8 rules. +## +## This file should be saved as /etc/audit/audit.rules. +## +## For audit 1.6.5 and higher +## + +## Remove any existing rules +-D + +## Increase buffer size to handle the increased number of messages. +## Feel free to increase this if the machine panic's +-b 8192 + +## Set failure mode to panic +-f 2 + +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + +## Audit 1, 1(a) Enough information to determine the date and time of +## action (e.g., common network time), the system locale of the action, +## the system entity that initiated or completed the action, the resources +## involved, and the action involved. + +## Things that could affect time +-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=time-change +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=time-change +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=time-change +-w /etc/localtime -p wa -k time-change + +## Things that could affect system locale +-a always,exit -F arch=b32 -S sethostname,setdomainname -F key=system-locale +-a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale +-w /etc/issue -p wa -k system-locale +-w /etc/issue.net -p wa -k system-locale +-w /etc/hosts -p wa -k system-locale +-w /etc/sysconfig/network -p wa -k system-locale +-a always,exit -F dir=/etc/NetworkManager/ -F perm=wa -F key=system-locale + +## Audit 1, 1(b) Successful and unsuccessful logons and logoffs. +## This is covered by patches to login, gdm, and openssh +## Might also want to watch these files if needing extra information +#-w /var/log/tallylog -p wa -k logins +#-w /var/run/faillock/ -p wa -k logins +#-w /var/log/lastlog -p wa -k logins +#-w /var/log/btmp -p wa -k logins +#-w /var/run/utmp -p wa -k logins + +## Audit 1, 1(c) Successful and unsuccessful accesses to +## security-relevant objects and directories, including +## creation, open, close, modification, and deletion. + +## unsuccessful creation +-a always,exit -F arch=b32 -S creat,link,mknod,mkdir,symlink,mknodat,linkat,symlinkat -F exit=-EACCES -F key=creation +-a always,exit -F arch=b64 -S mkdir,creat,link,symlink,mknod,mknodat,linkat,symlinkat -F exit=-EACCES -F key=creation +-a always,exit -F arch=b32 -S link,mkdir,symlink,mkdirat -F exit=-EPERM -F key=creation +-a always,exit -F arch=b64 -S mkdir,link,symlink,mkdirat -F exit=-EPERM -F key=creation + +## unsuccessful open +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F exit=-EACCES -F key=open +-a always,exit -F arch=b64 -S open,openat,open_by_handle_at -F exit=-EACCES -F key=open +-a always,exit -F arch=b32 -S open,openat,open_by_handle_at -F exit=-EPERM -F key=open +-a always,exit -F arch=b64 -S open,openat,open_by_handle_at -F exit=-EPERM -F key=open + +## unsuccessful close +-a always,exit -F arch=b32 -S close -F exit=-EIO -F key=close +-a always,exit -F arch=b64 -S close -F exit=-EIO -F key=close + +## unsuccessful modifications +-a always,exit -F arch=b32 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EACCES -F key=mods +-a always,exit -F arch=b64 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EACCES -F key=mods +-a always,exit -F arch=b32 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EPERM -F key=mods +-a always,exit -F arch=b64 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EPERM -F key=mods + +## unsuccessful deletion +-a always,exit -F arch=b32 -S unlink,rmdir,unlinkat -F exit=-EACCES -F key=delete +-a always,exit -F arch=b64 -S rmdir,unlink,unlinkat -F exit=-EACCES -F key=delete +-a always,exit -F arch=b32 -S unlink,rmdirunlinkat -F exit=-EPERM -F key=delete +-a always,exit -F arch=b64 -S rmdir,unlink,unlinkat -F exit=-EPERM -F key=delete + +## Audit 1, 1(d) Changes in user authenticators. +## Covered by patches to libpam, passwd, and shadow-utils +## Might also want to watch these files for changes +-w /etc/group -p wa -k auth +-w /etc/passwd -p wa -k auth +-w /etc/gshadow -p wa -k auth +-w /etc/shadow -p wa -k auth +-w /etc/security/opasswd -p wa -k auth + +## Audit 1, 1(e) The blocking or blacklisting of a user ID, +## terminal, or access port and the reason for the action. +## Covered by patches to pam_tally2 or pam_faillock and pam_limits + +## Audit 1, 1(f) Denial of access resulting from an excessive +## number of unsuccessful logon attempts. +## Covered by patches to pam_tally2 or pam_faillock + +## Audit 1, 2 Audit Trail Protection. The contents of audit trails +## shall be protected against unauthorized access, modification, +## or deletion. +## This should be covered by file permissions, but we can watch it +## to see any activity +-w /var/log/audit/ -k audit-logs + +## Not specifically required by NISPOM; but common sense items +## Optional - could indicate someone trying to do something bad or +## just debugging +#-a always,exit -F arch=b32 -S ptrace -F key=tracing +#-a always,exit -F arch=b64 -S ptrace -F key=tracing +#-a always,exit -F arch=b32 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x6 -F key=register-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x6 -F key=register-injection + +## Optional - might want to watch module insertion +#-w /sbin/insmod -p x -k modules +#-w /sbin/rmmod -p x -k modules +#-w /sbin/modprobe -p x -k modules +#-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b32 -S delete_module -F key=module-unload +#-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +## Optional - admin may be abusing power by looking in user's home dir +#-a always,exit -F dir=/home -F uid=0 -F auid>=1000 -F auid!=4294967295 -C auid!=obj_uid -F key=power-abuse + +## Optional - log container creation +#-a always,exit -F arch=b32 -S clone -F a0&0x2080505856 -F key=container-create +#-a always,exit -F arch=b64 -S clone -F a0&0x2080505856 -F key=container-create + +## Optional - watch for containers that may change their configuration +#-a always,exit -F arch=b32 -S unshare,setns -F key=container-config +#-a always,exit -F arch=b64 -S unshare,setns -F key=container-config + +## Put your own watches after this point +# -w /your-file -p rwxa -k mykey + +## Make the configuration immutable +#-e 2 diff --git a/framework/src/audit/contrib/plugin/Makefile b/framework/src/audit/contrib/plugin/Makefile new file mode 100644 index 00000000..4256c4d1 --- /dev/null +++ b/framework/src/audit/contrib/plugin/Makefile @@ -0,0 +1,7 @@ +CFLAGS=-g -W -Wall -Wundef +LIBS= -lauparse -laudit +all: + gcc $(CFLAGS) audisp-example.c -o audisp-example $(LIBS) + +clean: + rm -f audisp-example *.o diff --git a/framework/src/audit/contrib/plugin/audisp-example.c b/framework/src/audit/contrib/plugin/audisp-example.c new file mode 100644 index 00000000..6fcca1a1 --- /dev/null +++ b/framework/src/audit/contrib/plugin/audisp-example.c @@ -0,0 +1,229 @@ +/* audisp-example.c -- + * Copyright 2012 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + * This is a sample program to demonstrate several concepts of how to + * write an audispd plugin using libauparse. It can be tested by using a + * file of raw audit records. You can generate the test file like: + * + * ausearch --start today --raw > test.log. + * + * Then you can test this app by: cat test.log | ./audisp-example + * + * It will print things to stdout. In a real program, you wouldn't + * do anything with stdout since that is likely to be pointing to /dev/null. + * + * Excluding some init/destroy items you might need to add to main, the + * event_handler function is the main place that you would modify to do + * things specific to your plugin. + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "auparse.h" + +/* Global Data */ +static volatile int stop = 0; +static volatile int hup = 0; +static auparse_state_t *au = NULL; + +/* Local declarations */ +static void handle_event(auparse_state_t *au, + auparse_cb_event_t cb_event_type, void *user_data); + +/* + * SIGTERM handler + */ +static void term_handler( int sig ) +{ + stop = 1; +} + +/* + * SIGHUP handler: re-read config + */ +static void hup_handler( int sig ) +{ + hup = 1; +} + +static void reload_config(void) +{ + hup = 0; +} + +int main(int argc, char *argv[]) +{ + char tmp[MAX_AUDIT_MESSAGE_LENGTH+1]; + struct sigaction sa; + + /* Register sighandlers */ + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + /* Set handler for the ones we care about */ + sa.sa_handler = term_handler; + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = hup_handler; + sigaction(SIGHUP, &sa, NULL); + + /* Initialize the auparse library */ + au = auparse_init(AUSOURCE_FEED, 0); + if (au == NULL) { + printf("audisp-example is exiting due to auparse init errors"); + return -1; + } + auparse_add_callback(au, handle_event, NULL, NULL); + do { + fd_set read_mask; + struct timeval tv; + int retval; + + /* Load configuration */ + if (hup) { + reload_config(); + } + do { + tv.tv_sec = 5; + tv.tv_usec = 0; + FD_ZERO(&read_mask); + FD_SET(0, &read_mask); + if (auparse_feed_has_data(au)) + retval= select(1, &read_mask, NULL, NULL, &tv); + else + retval= select(1, &read_mask, NULL, NULL, NULL); + } while (retval == -1 && errno == EINTR && !hup && !stop); + + /* Now the event loop */ + if (!stop && !hup && retval > 0) { + if (fgets_unlocked(tmp, MAX_AUDIT_MESSAGE_LENGTH, + stdin)) { + auparse_feed(au, tmp, strnlen(tmp, + MAX_AUDIT_MESSAGE_LENGTH)); + } + } else if (retval == 0) + auparse_flush_feed(au); + if (feof(stdin)) + break; + } while (stop == 0); + + /* Flush any accumulated events from queue */ + auparse_flush_feed(au); + auparse_destroy(au); + if (stop) + printf("audisp-example is exiting on stop request\n"); + else + printf("audisp-example is exiting on stdin EOF\n"); + + return 0; +} + +/* This function shows how to dump a whole event by iterating over records */ +static void dump_whole_event(auparse_state_t *au) +{ + auparse_first_record(au); + do { + printf("%s\n", auparse_get_record_text(au)); + } while (auparse_next_record(au) > 0); + printf("\n"); +} + +/* This function shows how to dump a whole record's text */ +static void dump_whole_record(auparse_state_t *au) +{ + printf("%s: %s\n", audit_msg_type_to_name(auparse_get_type(au)), + auparse_get_record_text(au)); + printf("\n"); +} + +/* This function shows how to iterate through the fields of a record + * and print its name and raw value and interpretted value. */ +static void dump_fields_of_record(auparse_state_t *au) +{ + printf("record type %d(%s) has %d fields\n", auparse_get_type(au), + audit_msg_type_to_name(auparse_get_type(au)), + auparse_get_num_fields(au)); + + printf("line=%d file=%s\n", auparse_get_line_number(au), + auparse_get_filename(au) ? auparse_get_filename(au) : "stdin"); + + const au_event_t *e = auparse_get_timestamp(au); + if (e == NULL) { + printf("Error getting timestamp - aborting\n"); + return; + } + /* Note that e->sec can be treated as time_t data if you want + * something a little more readable */ + printf("event time: %u.%u:%lu, host=%s\n", (unsigned)e->sec, + e->milli, e->serial, e->host ? e->host : "?"); + auparse_first_field(au); + + do { + printf("field: %s=%s (%s)\n", + auparse_get_field_name(au), + auparse_get_field_str(au), + auparse_interpret_field(au)); + } while (auparse_next_field(au) > 0); + printf("\n"); +} + +/* This function receives a single complete event at a time from the auparse + * library. This is where the main analysis code would be added. */ +static void handle_event(auparse_state_t *au, + auparse_cb_event_t cb_event_type, void *user_data) +{ + int type, num=0; + + if (cb_event_type != AUPARSE_CB_EVENT_READY) + return; + + /* Loop through the records in the event looking for one to process. + We use physical record number because we may search around and + move the cursor accidentally skipping a record. */ + while (auparse_goto_record_num(au, num) > 0) { + type = auparse_get_type(au); + /* Now we can branch based on what record type we find. + This is just a few suggestions, but it could be anything. */ + switch (type) { + case AUDIT_AVC: + dump_fields_of_record(au); + break; + case AUDIT_SYSCALL: + dump_whole_record(au); + break; + case AUDIT_USER_LOGIN: + break; + case AUDIT_ANOM_ABEND: + break; + case AUDIT_MAC_STATUS: + dump_whole_event(au); + break; + default: + break; + } + num++; + } +} + diff --git a/framework/src/audit/contrib/plugin/audisp-example.conf b/framework/src/audit/contrib/plugin/audisp-example.conf new file mode 100644 index 00000000..e8a7b81e --- /dev/null +++ b/framework/src/audit/contrib/plugin/audisp-example.conf @@ -0,0 +1,10 @@ +# This file controls the configuration of the +# example syslog plugin. It simply takes events and writes +# them to syslog. + +active = no +direction = out +path = /sbin/audisp-example +type = always +args = 1 +format = string diff --git a/framework/src/audit/contrib/skeleton.c b/framework/src/audit/contrib/skeleton.c new file mode 100644 index 00000000..7e041042 --- /dev/null +++ b/framework/src/audit/contrib/skeleton.c @@ -0,0 +1,140 @@ +/* skeleton.c -- + * + * This is a sample program that you can customize to create your own audit + * event handler. It will be started by auditd via the dispatcher option in + * /etc/audit/auditd.conf. This program can be built as follows: + * + * gcc skeleton.c -o skeleton -laudit + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libaudit.h" + + +// Local data +static volatile int signaled = 0; +static int pipe_fd; +static const char *pgm = "skeleton"; + +// Local functions +static int event_loop(void); + +// SIGTERM handler +static void term_handler( int sig ) +{ + signaled = 1; +} + + +/* + * main is started by auditd. See dispatcher in auditd.conf + */ +int main(int argc, char *argv[]) +{ + struct sigaction sa; + + setlocale (LC_ALL, ""); + openlog(pgm, LOG_PID, LOG_DAEMON); + syslog(LOG_NOTICE, "starting..."); + +#ifndef DEBUG + // Make sure we are root + if (getuid() != 0) { + syslog(LOG_ERR, "You must be root to run this program."); + return 4; + } +#endif + + // register sighandlers + sa.sa_flags = 0 ; + sa.sa_handler = term_handler; + sigemptyset( &sa.sa_mask ) ; + sigaction( SIGTERM, &sa, NULL ); + sa.sa_handler = term_handler; + sigemptyset( &sa.sa_mask ) ; + sigaction( SIGCHLD, &sa, NULL ); + sa.sa_handler = SIG_IGN; + sigaction( SIGHUP, &sa, NULL ); + (void)chdir("/"); + + // change over to pipe_fd + pipe_fd = dup(0); + close(0); + open("/dev/null", O_RDONLY); + fcntl(pipe_fd, F_SETFD, FD_CLOEXEC); + + // Start the program + return event_loop(); +} + +static int event_loop(void) +{ + void *data; + struct iovec vec[2]; + struct audit_dispatcher_header hdr; + + // allocate data structures + data = malloc(MAX_AUDIT_MESSAGE_LENGTH); + if (data == NULL) { + syslog(LOG_ERR, "Cannot allocate buffer"); + return 1; + } + memset(data, 0, MAX_AUDIT_MESSAGE_LENGTH); + memset(&hdr, 0, sizeof(hdr)); + + do { + int rc; + struct timeval tv; + fd_set fd; + + tv.tv_sec = 1; + tv.tv_usec = 0; + FD_ZERO(&fd); + FD_SET(pipe_fd, &fd); + rc = select(pipe_fd+1, &fd, NULL, NULL, &tv); + if (rc == 0) + continue; + else if (rc == -1) + break; + + /* Get header first. it is fixed size */ + vec[0].iov_base = (void*)&hdr; + vec[0].iov_len = sizeof(hdr); + do { + rc = readv(fd, &vec[0], 1); + } while (rc < 0 && errno == EINTR); + + if (rc > 0) { + // Next payload + vec[1].iov_base = data; + vec[1].iov_len = hdr.size; + do { + rc = readv(fd, &vec[1], 1); + } while (rc < 0 && errno == EINTR); + } + if (rc <= 0) { + syslog(LOG_ERR, "rc == %d(%s)", rc, strerror(errno)); + continue; + } + + // Handle events here. Just for illustration, we print + // to syslog, but you will want to do something else. + syslog(LOG_NOTICE,"type=%d, payload size=%d", + hdr.type, hdr.size); + syslog(LOG_NOTICE,"data=\"%.*s\"", hdr.size, + (char *)data); + + } while(!signaled); + + return 0; +} + diff --git a/framework/src/audit/contrib/stig.rules b/framework/src/audit/contrib/stig.rules new file mode 100644 index 00000000..5a51d7f0 --- /dev/null +++ b/framework/src/audit/contrib/stig.rules @@ -0,0 +1,193 @@ +## This file contains the auditctl rules that are loaded +## whenever the audit daemon is started via the initscripts. +## The rules are simply the parameters that would be passed +## to auditctl. +## +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## Set failure mode to panic +-f 2 + +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + +## NOTE: +## 1) if this is being used on a 32 bit machine, comment out the b64 lines +## 2) These rules assume that login under the root account is not allowed. +## 3) It is also assumed that 500 represents the first usable user account. To +## be sure, look at UID_MIN in /etc/login.defs. +## 4) If these rules generate too much spurious data for your tastes, limit the +## the syscall file rules with a directory, like -F dir=/etc +## 5) You can search for the results on the key fields in the rules +## +## +## (GEN002880: CAT II) The IAO will ensure the auditing software can +## record the following for each audit event: +##- Date and time of the event +##- Userid that initiated the event +##- Type of event +##- Success or failure of the event +##- For I&A events, the origin of the request (e.g., terminal ID) +##- For events that introduce an object into a user’s address space, and +## for object deletion events, the name of the object, and in MLS +## systems, the object’s security level. +## +## Things that could affect time +-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=time-change +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=time-change +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=time-change +-w /etc/localtime -p wa -k time-change + +## Things that affect identity +-w /etc/group -p wa -k identity +-w /etc/passwd -p wa -k identity +-w /etc/gshadow -p wa -k identity +-w /etc/shadow -p wa -k identity +-w /etc/security/opasswd -p wa -k identity + +## Things that could affect system locale +-a always,exit -F arch=b32 -S sethostname,setdomainname -F key=system-locale +-a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale +-w /etc/issue -p wa -k system-locale +-w /etc/issue.net -p wa -k system-locale +-w /etc/hosts -p wa -k system-locale +-w /etc/sysconfig/network -p wa -k system-locale +-a always,exit -F dir=/etc/NetworkManager/ -F perm=wa -F key=system-locale + +## Things that could affect MAC policy +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F key=MAC-policy + + +## (GEN002900: CAT III) The IAO will ensure audit files are retained at +## least one year; systems containing SAMI will be retained for five years. +## +## Site action - no action in config files + +## (GEN002920: CAT III) The IAO will ensure audit files are backed up +## no less than weekly onto a different system than the system being +## audited or backup media. +## +## Can be done with cron script + +## (GEN002700: CAT I) (Previously – G095) The SA will ensure audit data +## files have permissions of 640, or more restrictive. +## +## Done automatically by auditd + +## (GEN002720-GEN002840: CAT II) (Previously – G100-G106) The SA will +## configure the auditing system to audit the following events for all +## users and root: +## +## - Logon (unsuccessful and successful) and logout (successful) +## +## Handled by pam, sshd, login, and gdm +## Might also want to watch these files if needing extra information +#-w /var/log/tallylog -p wa -k logins +#-w /var/run/faillock/ -p wa -k logins +#-w /var/log/lastlog -p wa -k logins + + +##- Process and session initiation (unsuccessful and successful) +## +## The session initiation is audited by pam without any rules needed. +## Might also want to watch this file if needing extra information +#-w /var/run/utmp -p wa -k session +#-w /var/log/btmp -p wa -k session +#-w /var/log/wtmp -p wa -k session + +##- Discretionary access control permission modification (unsuccessful +## and successful use of chown/chmod) +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=500 -F auid!=4294967295 -F key=perm_mod +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=500 -F auid!=4294967295 -F key=perm_mod +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=500 -F auid!=4294967295 -F key=perm_mod +-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=500 -F auid!=4294967295 -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=500 -F auid!=4294967295 -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=500 -F auid!=4294967295 -F key=perm_mod + +##- Unauthorized access attempts to files (unsuccessful) +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -F key=access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -F key=access +-a always,exit -F arch=b64 -S open,truncate,ftruncate,creat,openat,open_by_handle_at -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -F key=access +-a always,exit -F arch=b64 -S open,truncate,ftruncate,creat,openat,open_by_handle_at -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -F key=access + +##- Use of privileged commands (unsuccessful and successful) +## use find /bin -type f -perm -04000 2>/dev/null and put all those files in a rule like this +-a always,exit -F path=/bin/ping -F perm=x -F auid>=500 -F auid!=4294967295 -F key=privileged + +##- Use of print command (unsuccessful and successful) + +##- Export to media (successful) +## You have to mount media before using it. You must disable all automounting +## so that its done manually in order to get the correct user requesting the +## export +-a always,exit -F arch=b32 -S mount -F auid>=500 -F auid!=4294967295 -F key=export +-a always,exit -F arch=b64 -S mount -F auid>=500 -F auid!=4294967295 -F key=export + +##- System startup and shutdown (unsuccessful and successful) + +##- Files and programs deleted by the user (successful and unsuccessful) +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>=500 -F auid!=4294967295 -F key=delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=500 -F auid!=4294967295 -F key=delete + +##- All system administration actions +##- All security personnel actions +## +## Look for pam_tty_audit and add it to your login entry point's pam configs. +## If that is not found, use sudo which should be patched to record its +## commands to the audit system. Do not allow unrestricted root shells or +## sudo cannot record the action. +-w /etc/sudoers -p wa -k actions +-w /etc/sudoers.d/ -p wa -k actions + +## (GEN002860: CAT II) (Previously – G674) The SA and/or IAO will +##ensure old audit logs are closed and new audit logs are started daily. +## +## Site action. Can be assisted by a cron job + +## Not specifically required by the STIG; but common sense items +## Optional - could indicate someone trying to do something bad or +## just debugging +#-a always,exit -F arch=b32 -S ptrace -F key=tracing +#-a always,exit -F arch=b64 -S ptrace -F key=tracing +#-a always,exit -F arch=b32 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x4 -F key=code-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x5 -F key=data-injection +#-a always,exit -F arch=b32 -S ptrace -F a0=0x6 -F key=register-injection +#-a always,exit -F arch=b64 -S ptrace -F a0=0x6 -F key=register-injection + +## Optional - might want to watch module insertion +#-w /sbin/insmod -p x -k modules +#-w /sbin/rmmod -p x -k modules +#-w /sbin/modprobe -p x -k modules +#-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +#-a always,exit -F arch=b32 -S delete_module -F key=module-unload +#-a always,exit -F arch=b64 -S delete_module -F key=module-unload + +## Optional - admin may be abusing power by looking in user's home dir +#-a always,exit -F dir=/home -F uid=0 -F auid>=500 -F auid!=4294967295 -C auid!=obj_uid -F key=power-abuse + +## Optional - log container creation +#-a always,exit -F arch=b32 -S clone -F a0&0x7C020000 -F key=container-create +#-a always,exit -F arch=b64 -S clone -F a0&0x7C020000 -F key=container-create + +## Optional - watch for containers that may change their configuration +#-a always,exit -F arch=b32 -S unshare,setns -F key=container-config +#-a always,exit -F arch=b64 -S unshare,setns -F key=container-config + +## Put your own watches after this point +# -w /your-file -p rwxa -k mykey + +## Make the configuration immutable - reboot is required to change audit rules +-e 2 + diff --git a/framework/src/audit/depcomp b/framework/src/audit/depcomp new file mode 100755 index 00000000..4ebd5b3a --- /dev/null +++ b/framework/src/audit/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/framework/src/audit/docs/Makefile.am b/framework/src/audit/docs/Makefile.am new file mode 100644 index 00000000..3f748806 --- /dev/null +++ b/framework/src/audit/docs/Makefile.am @@ -0,0 +1,59 @@ +# Makefile.am -- +# Copyright 2004-09,2012,2014 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.rej *.orig + +EXTRA_DIST = $(man_MANS) + +man_MANS = audit_add_rule_data.3 audit_add_watch.3 auditctl.8 auditd.8 \ +auditd.conf.5 audit_delete_rule_data.3 audit_detect_machine.3 \ +audit_encode_nv_string.3 audit_getloginuid.3 \ +audit_get_reply.3 auparse_goto_record_num.3 \ +audit_log_acct_message.3 audit_log_user_avc_message.3 \ +audit_log_user_command.3 audit_log_user_comm_message.3 \ +audit_log_user_message.3 audit_log_semanage_message.3 \ +audit_open.3 audit_request_rules_list_data.3 \ +audit_request_signal_info.3 audit_request_status.3 audit.rules.7 \ +audit_set_backlog_limit.3 audit_set_enabled.3 audit_set_failure.3 \ +audit_setloginuid.3 audit_set_pid.3 audit_set_rate_limit.3 \ +audit_update_watch_perms.3 auparse_add_callback.3 \ +auparse_destroy.3 auparse_feed.3 auparse_feed_has_data.3 auparse_find_field.3 \ +auparse_find_field_next.3 auparse_first_field.3 auparse_first_record.3 \ +auparse_flush_feed.3 auparse_get_field_int.3 auparse_get_field_name.3 \ +auparse_get_field_str.3 auparse_get_field_type.3 auparse_get_filename.3 \ +auparse_get_line_number.3 auparse_get_milli.3 \ +auparse_get_node.3 auparse_get_num_fields.3 \ +auparse_get_num_records.3 auparse_get_record_text.3 \ +auparse_get_serial.3 auparse_get_time.3 auparse_get_timestamp.3 \ +auparse_get_type.3 auparse_init.3 auparse_interpret_field.3 \ +auparse_next_event.3 auparse_next_field.3 auparse_next_record.3 \ +auparse_node_compare.3 auparse_reset.3 auparse_timestamp_compare.3 \ +ausearch-expression.5 \ +aureport.8 ausearch.8 ausearch_add_item.3 ausearch_add_interpreted_item.3 \ +ausearch_add_expression.3 ausearch_add_timestamp_item.3 ausearch_add_regex.3 \ +ausearch_add_timestamp_item_ex.3 ausearch_clear.3 \ +ausearch_next_event.3 ausearch_set_stop.3 \ +autrace.8 get_auditfail_action.3 set_aumessage_mode.3 \ +audispd.8 audispd.conf.5 audispd-zos-remote.8 libaudit.conf.5 \ +augenrules.8 audit_set_backlog_wait_time.3 \ +zos-remote.conf.5 + diff --git a/framework/src/audit/docs/audispd-zos-remote.8 b/framework/src/audit/docs/audispd-zos-remote.8 new file mode 100644 index 00000000..b6a742d5 --- /dev/null +++ b/framework/src/audit/docs/audispd-zos-remote.8 @@ -0,0 +1,241 @@ +.\" Copyright (c) International Business Machines Corp., 2007 +.\" +.\" This program is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation; either version 2 of +.\" the License, or (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +.\" the GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, +.\" MA 02111-1307 USA +.\" +.\" Changelog: +.\" 2007-10-06, created by Klaus Heinrich Kiwi +.\" +.TH AUDISP-RACF 8 "Oct 2007" "IBM" "System Administration Utilities" +.SH NAME +audispd\-zos\-remote \- z/OS Remote-services Audit dispatcher plugin +.SH SYNOPSIS +.B audispd\-zos\-remote [ +.I config-file +.B ] +.SH DESCRIPTION +.B audispd\-zos\-remote +is a remote-auditing plugin for the Audit subsystem. It should be started by the +.BR audispd (8) +daemon and will forward all incoming audit events, as they happen, to a configured z/OS SMF (Service Management Facility) database, through an IBM Tivoli Directory Server (ITDS) set for Remote Audit service. +See +.B SMF MAPPING +section below for more information about the resulting SMF record format. + +.BR audispd (8) +must be configured to start the plugin. This is done by a configuration file usually located at +.IR /etc/audisp/plugins.d/audispd\-zos\-remote.conf , +but multiple instances can be spawned by having multiple configuration files in +.I /etc/audisp/plugins.d +for the same plugin executable (see +.BR audispd (8)). + +Each instance needs a configuration file, located by default at +.IR /etc/audisp/zos\-remote.conf . +Check +.BR zos\-remote.conf (5) +for details about the plugin configuration. + +.SH OPTIONS +.IP config-file +Use an alternate configuration file instead of +.IR /etc/audisp/zos\-remote.conf . + +.SH SIGNALS +.B audispd\-zos\-remote +reacts to SIGTERM and SIGHUP signals (according to the +.BR audispd (8) +specification): +.TP +.B SIGHUP +Instructs the +.B audispd\-zos\-remote +plugin to re-read it's configuration and flush existing network connections. +.TP +.B SIGTERM +Performs a clean exit. +.B audispd\-zos\-remote +will wait up to 10 seconds if there are queued events to be delivered, dropping any remaining queued events after that time. + +.SH IBM z/OS ITDS Server and RACF configuration +In order to use this plugin, you must have an IBM z/OS v1R8 (or higher) server with IBM Tivoli Directory Server (ITDS) configured for Remote Audit service. For more detailed information about how to configure the z/OS server for Remote Auditing, refer to +.B z/OS V1R8.0-9.0 Intergrated Security Services Enterprise Identity Mapping (EIM) Guide and Reference +.nf +.RI ( http://publibz.boulder.ibm.com/cgi\-bin/bookmgr_OS390/FRAMESET/EIMA1140/CCONTENTS?DT=20070827115119 ), +chapter "2.0 - Working with remote services". +.fi + +.SS Enable ITDS to process Remote Audit requests +To enable ITSD to process Remote Audit requests, the user ID associated with ITDS must be granted READ access to the IRR.AUDITX FACILITY Class profile (the profile used to protect the R_Auditx service). This user ID can usually be found in the STARTED Class profile for the ITDS started procedure. If the identity associated with ITDS is +.IR ITDSUSER , +the administrator can configure RACF to grant Remote Auditing processing to ITDS with the following TSO commands: +.TP +.I TSO Commands: Grant ITDSUSER READ access to IRR.AUDITX FACILITY Class profile +.nf +rdefine FACILITY IRR.RAUDITX uacc(none) +permit IRR.RAUDITX class(FACILITY) id(ITDSUSER) access(READ) +.fi + +.SS Create/enable RACF user ID to perform Remote Audit requests +A z/OS RACF user ID is needed by the plugin - Every Audit request performed by the plugin will use a RACF user ID, as configured in the plugin configuration +.BR zos\-remote.conf (5). +This user ID needs READ access to FACILITY Class resource IRR.LDAP.REMOTE.AUDIT. If the user ID is +.IR BINDUSER , +the administrator can configure RACF to enable this user to perform Remote Auditing requests with the following TSO commands: +.TP +.I TSO Commands: Enable BINDUSER to perform Remote Audit requests +.nf +rdefine FACILITY IRR.LDAP.REMOTE.AUDIT uacc(none) +permit IRR.LDAP.REMOTE.AUDIT class(FACILITY) id(BINDUSER) access(READ) +.fi + +.SS Add @LINUX Class to RACF +When performing remote auditing requests, the +.B audispd\-zos\-remote +plugin will use the special +.B @LINUX +.I CDT Class +and the audit record type (eg.: +.BR SYSCALL , +.BR AVC , +.BR PATH ...) +as the +.I CDT Resource Class +for all events processed. +To make sure events are logged, the RACF server must be configured with a Dynamic CDT Class named +.B @LINUX +with correct sizes and attributes. The following TSO commands can be used to add this class: +.TP +.I TSO Commands: Add @LINUX CDT Class +.nf +rdefine cdt @LINUX cdtinfo(posit(493) FIRST(alpha,national,numeric,special) OTHER(alpha,national,numeric,special) RACLIST(REQUIRED) case(asis) generic(allowed) defaultuacc(none) maxlength(246)) +setr classact(cdt) +setr raclist(cdt) +setr raclist(cdt) refresh +setr classact(@LINUX) +setr raclist(@LINUX) +setr generic(@LINUX) +.fi + +.SS Add profiles to the @LINUX Class +Once the CDT Class has been defined, you can add profiles to it, specifying resources (wildcards allowed) to log or ignore. The following are examples: +.TP +.I TSO Commands: Log only AVC records (One generic and one discrete profile): +.nf +rdefine @LINUX * uacc(none) audit(none(read)) +rdefine @LINUX AVC uacc(none) audit(all(read)) +setr raclist(@LINUX) refresh +.fi + +.TP +.I TSO Commands: Log everything (One generic profile): +.nf +rdefine @LINUX * uacc(none) audit(all(read)) +setr raclist(@LINUX) refresh +.fi + +.P +Resources always match the single profile with the +.I best +match. + +There are many other ways to define logging in RACF. Please refer to the server documentation for more details. + +.SH SMF Mapping +The ITDS Remote Audit service will cut SMF records of type 83 subtype 4 everytime it processes a request. This plugin will issue a remote audit request for every incoming Linux Audit record (meaning that one Linux record will map to one SMF record), and fill this type's records with the following: +.SS Link Value +The Linux event serial number, encoded in network-byte order hexadecimal representation. Records within the same Event share the same Link Value. +.SS Violation +Always zero (0) - +.I False +.SS Event Code +Always two (2) - +.I Authorization +event +.SS Event Qualifier +Zero (0) - +.IR Success , +if the event reported +.B success=yes +or +.BR res=success , +Three (3) - +.IR Fail , +if the event reported +.B success=no +or +.BR res=failed , +or One (1) - +.I Info +otherwise. +.SS Class +Always +.I @LINUX +.SS Resource +The Linux record type for the processed record. e.g.: +.IR SYSCALL , AVC , PATH , CWD +etc. +.SS Log String +Textual message bringing the RACF user ID used to perform the request, plus the Linux hostname and the record type for the first record in the processed event. e.g.: +.I Remote audit request from RACFUSER. Linux (hostname.localdomain):USER_AUTH +.SS Data Field List +Also known as +.IR relocates , +this list will bring all the field names and values in a +.B fieldname=value +format, as a type 114 +.RB ( "Appication specific Data" ) +relocate. The plug-in will try to interpret those fields (i.e.: use human-readable username +.B root +instead of numeric userid +.BR 0 ) +whenever possible. Currently, this plugin will also add a relocate type 113 +.RB ( "Date And Time Security Event Occurred" ) +with the Event Timestamp in the format as returned by +.BR ctime (3). + +.SH ERRORS +Errors and warnings are reported to syslog (under DAEMON facility). In situations where the event was submitted but the z/OS server returned an error condition, the logged message brings a name followed by a human-readable description. Below are some common errors conditions: + +.TP +.B NOTREQ - No logging required +Resource (audit record type) is not set to be logged in the RACF server - The @LINUX Class profile governing this audit record type is set to ignore. See +.B IBM z/OS RACF Server configuration +.TP +.B UNDETERMINED - Undetermined result +No profile found for specified resource. There is no @LINUX Class configured or no @LINUX Class profile associated with this audit record type. See +.B IBM z/OS RACF Server configuration +.TP +.B UNAUTHORIZED - The user does not have authority the R_auditx service +The user ID associated with the ITDS doesn't have READ access to the IRR.AUDITX FACILITY Class profile. See +.B IBM z/OS RACF Server configuration +.TP +.B UNSUF_AUTH - The user has unsuficient authority for the requested function +The RACF user ID used to perform Remote Audit requests (as configured in +.BR zos-remote.conf (5)) +don't have access to the IRR.LDAP.REMOTE.AUDIT FACILITY Class profile. See +.B IBM z/OS RACF Server configuration + +.SH BUGS +The plugin currently does remote auditing in a best-effort basis, and will dischard events in case the z/OS server cannot be contacted (network failures) or in any other case that event submission fails. + +.SH FILES +/etc/audisp/plugins.d/audispd\-zos\-remote.conf +/etc/audisp/zos\-remote.conf +.SH "SEE ALSO" +.BR auditd (8), +.BR zos\-remote.conf (5). +.SH AUTHOR +Klaus Heinrich Kiwi diff --git a/framework/src/audit/docs/audispd.8 b/framework/src/audit/docs/audispd.8 new file mode 100644 index 00000000..e4333248 --- /dev/null +++ b/framework/src/audit/docs/audispd.8 @@ -0,0 +1,60 @@ +.TH AUDISPD: "8" "Sept 2007" "Red Hat" "System Administration Utilities" +.SH NAME +audispd \- an event multiplexor +.SH SYNOPSIS +.B audispd +.SH DESCRIPTION +\fBaudispd\fP is an audit event multiplexor. It has to be started by the audit daemon in order to get events. It takes audit events and distributes them to child programs that want to analyze events in realtime. When the audit daemon receives a SIGTERM or SIGHUP, it passes that signal to the dispatcher, too. The dispatcher in turn passes those signals to its child processes. + +The child programs install a configuration file in a plugins directory, \fI/etc/audisp/plugins.d\fP. Filenames are not allowed to have more than one '.' in the name or it will be treated as a backup copy and skipped. Options are given one per line with an equal sign between the keyword and its value. The available options are as follows: + +.TP +.I active +The options for this are +.IR yes +or +.IR no. +.TP +.I direction +The option is dictated by the plugin. +.IR In +or +.IR out +are the only choices. You cannot make a plugin operate in a way it wasn't designed just by changing this option.This option is to give a clue to the event dispatcher about which direction events flow. NOTE: inbound events are not supported yet. +.TP +.I path +This is the absolute path to the plugin executable. In the case of internal plugins, it would be the name of the plugin. +.TP +.I type +This tells the dispatcher how the plugin wants to be run. Choices are +.IR builtin +and +.IR always. +.IR Builtin +should always be given for plugins that are internal to the audit event dispatcher. These are af_unix and syslog. The option +.IR always +should be given for most if not all plugins. The default setting is +.IR always. +.TP +.I args +This allows you to pass arguments to the child program. Generally plugins do not take arguments and have their own config file that instructs them how they should be configured. At the moment, there is a limit of 2 args. +.TP +.I format +The valid options for this are +.IR binary +and +.IR string. +.IR Binary +passes the data exactly as the audit event dispatcher gets it from the audit daemon. The +.IR string +option tells the dispatcher to completely change the event into a string suitable for parsing with the audit parsing library. The default value is +.IR string. + +.SH FILES +/etc/audisp/audispd.conf +/etc/audisp/plugins.d +.SH "SEE ALSO" +.BR audispd.conf (5), +.BR auditd (8). +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audispd.conf.5 b/framework/src/audit/docs/audispd.conf.5 new file mode 100644 index 00000000..5955a8fe --- /dev/null +++ b/framework/src/audit/docs/audispd.conf.5 @@ -0,0 +1,50 @@ +.TH AUDISPD.CONF: "5" "March 2014" "Red Hat" "System Administration Utilities" +.SH NAME +audispd.conf \- the audit event dispatcher configuration file +.SH DESCRIPTION +\fBaudispd.conf\fP is the file that controls the configuration of the audit event dispatcher. Each line should contain one configuration keyword, an equal sign, and then followed by appropriate configuration information. All option names and values are case insensitive. The keywords recognized are listed and described below. Each line should be limited to 160 characters or the line will be skipped. You may add comments to the file by starting the line with a '#' character. + +.TP +.I q_depth +This is a numeric value that tells how big to make the internal queue of the audit event dispatcher. A bigger queue lets it handle a flood of events better, but could hold events that are not processed when the daemon is terminated. If you get messages in syslog about events getting dropped, increase this value. The default value is 80. +.TP +.I overflow_action +This option determines how the daemon should react to overflowing its internal queue. When this happens, it means that more events are being received than it can get rid of. This error means that it is going to lose the current event its trying to dispatch. It has the following choices: +.IR ignore ", " syslog ", " suspend ", " single ", and " halt ". +If set to +.IR ignore , +the audisp daemon does nothing. +.I syslog +means that it will issue a warning to syslog. +.I suspend +will cause the audisp daemon to stop processing events. The daemon will still be alive. The +.I single +option will cause the audisp daemon to put the computer system in single user mode. +.I halt +option will cause the audisp daemon to shutdown the computer system. +.TP +.I priority_boost +This is a non-negative number that tells the audit event dispatcher how much of a priority boost it should take. This boost is in addition to the boost provided from the audit daemon. The default is 4. No change is 0. +.TP +.I max_restarts +This is a non-negative number that tells the audit event dispatcher how many times it can try to restart a crashed plugin. The default is 10. +.TP +.I name_format +This option controls how computer node names are inserted into the audit event stream. It has the following choices: +.IR none ", " hostname ", " fqd ", " numeric ", and " user ". +.IR None +means that no computer name is inserted into the audit event. +.IR hostname +is the name returned by the gethostname syscall. The +.IR fqd +means that it takes the hostname and resolves it with dns for a fully qualified domain name of that machine. +.IR Numeric +is similar to fqd except it resolves the IP address of the machine. +.IR User +is an admin defined string from the name option. The default value is +.IR none ". +.TP +.I name +This is the admin defined string that identifies the machine if user is given as the name_format option. +.SH "SEE ALSO" +.BR audispd (8) diff --git a/framework/src/audit/docs/audit.rules.7 b/framework/src/audit/docs/audit.rules.7 new file mode 100644 index 00000000..24e467c9 --- /dev/null +++ b/framework/src/audit/docs/audit.rules.7 @@ -0,0 +1,171 @@ +.TH AUDIT.RULES: "7" "Aug 2014" "Red Hat" "System Administration Utilities" +.SH NAME +audit.rules \- a set of rules loaded in the kernel audit system +.SH DESCRIPTION +\fBaudit.rules\fP is a file containing audit rules that will be loaded by the audit daemon's init script whenever the daemon is started. The auditctl program is used by the initscripts to perform this operation. The syntax for the rules is essentially the same as when typing in an auditctl command at a shell prompt except you do not need to type the auditctl command name since that is implied. The audit rules come in 3 varieties: +.IR control ", " file ", and " syscall ". + +.SS Control +Control commands generally involve configuring the audit system rather than telling it what to watch for. These commands typically include deleting all rules, setting the size of the kernel's backlog queue, setting the failure mode, setting the event rate limit, or to tell auditctl to ignore syntax errors in the rules and continue loading. Generally, these rules are at the top of the rules file. + +.SS File System +File System rules are sometimes called watches. These rules are used to audit access to particular files or directories that you may be interested in. If the path given in the rule is a directory, then the rule used is recursive to the bottom of the directory tree excluding any directories that may be mount points. The syntax of these rules generally follow this format: + +.nf +.B \-w path-to-file \-p permissions \-k keyname +.fi + +where the permission are any one of the following: + +.RS +.TP 2 +.B r +- read of the file +.TP +.B w +- write to the file +.TP +.B x +- execute the file +.TP +.B a +- change in the file's attribute +.RE +.SS System Call +The system call rules are loaded into a matching engine that intercepts each syscall that all programs on the system makes. Therefore it is very important to only use syscall rules when you have to since these affect performance. The more rules, the bigger the performance hit. You can help the performance, though, by combining syscalls into one rule whenever possible. + +The Linux kernel has 4 rule matching lists or filters as they are sometimes called. They are: task, exit, user, and exclude. The task list is checked only during the fork or clone syscalls. It is rarely used in practice. + +The exit filter is the place where all syscall and file system audit requests are evaluated. + +The user filter is used to filter (remove) some events that originate in user space. By default, any event originating in user space is allowed. So, if there are some events that you do not want to see, then this is a place where some can be removed. See auditctl(8) for fields that are valid. + +The exclude filter is used to exclude certain events from being emitted. The msgtype field is used to tell the kernel which message types you do not want to record. This filter can remove the event as a whole and is not selective about any other attribute. The user and exit filters are better suited to selectively auditing events. + +Syscall rules take the general form of: + +.nf +.B \-a action,list \-S syscall \-F field=value \-k keyname +.fi + +The +.B \-a +option tells the kernel's rule matching engine that we want to append a rule at the end of the rule list. But we need to specify which rule list it goes on and what action to take when it triggers. Valid actions are: + +.RS +.TP 7 +.B always +- always create an event +.TP +.B never +- never create an event +.RE + +The action and list are separated by a comma but no space in between. Valid lists are: +.IR task ", " exit ", " user ", and " exclude ". Their meaning was explained earlier. + +Next in the rule would normally be the +.B \-S +option. This field can either be the syscall name or number. For readability, the name is almost always used. You may give more than one syscall in a rule by specifying another +.B \-S +option. When sent into the kernel, all syscall fields are put into a mask so that one compare can determine if the syscall is of interest. So, adding multiple syscalls in one rule is very efficient. When you specify a syscall name, auditctl will look up the name and get its syscall number. This leads to some problems on bi-arch machines. The 32 and 64 bit syscall numbers sometimes, but not always, line up. So, to solve this problem, you would generally need to break the rule into 2 with one specifying \-F arch=b32 and the other specifying \-F arch=b64. This needs to go in front of the +.B \-S +option so that auditctl looks at the right lookup table when returning the number. + +After the syscall is specified, you would normally have one or more +.B \-F +options that fine tune what to match against. Rather than list all the valid field types here, the reader should look at the auditctl man page which has a full listing of each field and what it means. But its worth mentioning a couple things. + +The audit system considers uids to be unsigned numbers. The audit system uses the number \-1 to indicate that a loginuid is not set. This means that when its printed out, it looks like 4294967295. If you write a rule that you wanted try to get the valid users of the system, you need to look in /etc/login.defs to see where user accounts start. For example, if UID_MIN is 500, then you would also need to take into account that the unsigned representation of \-1 is higher than 500. So you would address this with the following piece of a rule: + +.nf +\-F auid>=500 \-F auid!=4294967295 +.fi + +These individual checks are "anded" and both have to be true. + +The last thing to know about syscall rules is that you can add a key field which is a free form text string that you want inserted into the event to help identify its meaning. This is discussed in more detail in the NOTES section. + +.SH NOTES +The purpose of auditing is to be able to do an investigation periodically or whenever an incident occurs. A few simple steps in planning up front will make this job easier. The best advice is to use keys in both the watches and system call rules to give the rule a meaning. If rules are related or together meet a specific requirement, then give them a common key name. You can use this during your investigation to select only results with a specific meaning. + +When doing an investigation, you would normally start off with the main aureport output to just get an idea about what is happening on the system. This report mostly tells you about events that are hard coded by the audit system such as login/out, uses of authentication, system anomalies, how many users have been on the machine, and if SE Linux has detected any AVCs. + +.nf +aureport \-\-start this-week +.fi + +After looking at the report, you probably want to get a second view about what rules you loaded that have been triggering. This is where keys become important. You would generally run the key summary report like this: + +.nf +aureport \-\-start this-week \-\-key \-\-summary +.fi + +This will give an ordered listing of the keys associated with rules that have been triggering. If, for example, you had a syscall audit rule that triggered on the failure to open files with EPERM that had a key field of access like this: + +.nf +\-a always,exit \-F arch=b64 \-S open \-S openat \-F exit=\-EPERM \-k access +.fi + +Then you can isolate these failures with ausearch and pipe the results to aureport for display. Suppose your investigation noticed a lot of the access denied events. If you wanted to see the files that unauthorized access has been attempted, you could run the following command: + +.nf +ausearch \-\-start this-week \-k access \-\-raw | aureport \-\-file \-\-summary +.fi + +This will give an ordered list showing which files are being accessed with the EPERM failure. Suppose you wanted to see which users might be having failed access, you would run the following command: + +.nf +ausearch \-\-start this-week \-k access \-\-raw | aureport \-\-user \-\-summary +.fi + +If your investigation showed a lot of failed accesses to a particular file, you could run the following report to see who is doing it: + +.fi +ausearch \-\-start this-week \-k access \-f /path-to/file \-\-raw | aureport \-\-user \-i +.fi + +This report will give you the individual access attempts by person. If you needed to see the actual audit event that is being reported, you would look at the date, time, and event columns. Assuming the event was 822 and it occurred at 2:30 on 09/01/2009 and you use the en_US.utf8 locale, the command would look something like this: + +.nf +ausearch \-\-start 09/01/2009 02:30 \-a 822 \-i \-\-just\-one +.fi + +This will select the first event from that day and time with the matching event id and interpret the numeric values into human readable values. + +The most important step in being able to do this kind of analysis is setting up key fields when the rules were originally written. It should also be pointed out that you can have more than one key field associated with any given rule. + +.SH TROUBLESHOOTING +If you are not getting events on syscall rules that you think you should, try running a test program under strace so that you can see the syscalls. There is a chance that you might have identified the wrong syscall. + +If you get a warning from auditctl saying, "32/64 bit syscall mismatch in line XX, you should specify an arch". This means that you specified a syscall rule on a bi-arch system where the syscall has a different syscall number for the 32 and 64 bit interfaces. This means that on one of those interfaces you are likely auditing the wrong syscall. To solve the problem, re-write the rule as two rules specifying the intended arch for each rule. For example, + +.nf +\-always,exit \-S openat \-k access +.fi + +would be rewritten as + +.nf +\-always,exit \-F arch=b32 \-S openat \-k access +\-always,exit \-F arch=b64 \-S openat \-k access +.fi + +If you get a warning that says, "entry rules deprecated, changing to exit rule". This means that you have a rule intended for the entry filter, but that filter is no longer available. Auditctl moved your rule to the exit filter so that it's not lost. But to solve this so that you do not get the warning any more, you need to change the offending rule from entry to exit. + +.SH EXAMPLES +The following rule shows how to audit failed access to files due to permission problems. Note that it takes two rules for each arch ABI to audit this since file access can fail with two different failure codes indicating permission problems. + +.nf +.B \-a always,exit \-F arch=b32 \-S open \-S openat \-F exit=\-EACCES \-k access +.B \-a always,exit \-F arch=b32 \-S open \-S openat \-F exit=\-EPERM \-k access +.B \-a always,exit \-F arch=b64 \-S open \-S openat \-F exit=\-EACCES \-k access +.B \-a always,exit \-F arch=b64 \-S open \-S openat \-F exit=\-EPERM \-k access +.fi + +.SH "SEE ALSO" +.BR auditctl (8), +.BR auditd (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_add_rule_data.3 b/framework/src/audit/docs/audit_add_rule_data.3 new file mode 100644 index 00000000..2321f391 --- /dev/null +++ b/framework/src/audit/docs/audit_add_rule_data.3 @@ -0,0 +1,49 @@ +.TH "AUDIT_ADD_RULE_DATA" "3" "Aug 2009" "Red Hat" "Linux Audit API" +.SH NAME +audit_add_rule_data \- Add new audit rule +.SH "SYNOPSIS" +.B #include +.sp +int audit_add_rule_data (int fd, struct audit_rule_data *rule, int flags, int action); + +.SH "DESCRIPTION" + +audit_add_rule adds an audit rule previously constructed with audit_rule_fieldpair_data(3) to one of several kernel event filters. The filter is specified by the flags argument. Possible values for flags are: + +.TP 3 +\(bu +AUDIT_FILTER_USER - Apply rule to userspace generated messages. +.TP +\(bu +AUDIT_FILTER_TASK - Apply rule at task creation (not syscall). +.TP +\(bu +AUDIT_FILTER_EXIT - Apply rule at syscall exit. +.TP +\(bu +AUDIT_FILTER_TYPE - Apply rule at audit_log_start. +.LP + +.PP +The rule's action has two possible values: + +.TP 3 +\(bu +AUDIT_NEVER - Do not build context if rule matches. +.TP +\(bu +AUDIT_ALWAYS - Generate audit record if rule matches. +.LP + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_rule_fieldpair_data(3), +.BR audit_delete_rule_data (3), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb. diff --git a/framework/src/audit/docs/audit_add_watch.3 b/framework/src/audit/docs/audit_add_watch.3 new file mode 100644 index 00000000..66616e76 --- /dev/null +++ b/framework/src/audit/docs/audit_add_watch.3 @@ -0,0 +1,23 @@ +.TH "AUDIT_ADD_WATCH" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +audit_add_watch \- create a rule layout for a watch +.SH "SYNOPSIS" +.B #include +.sp +int audit_add_watch(struct audit_rule_data **rulep, const char *path); + +.SH "DESCRIPTION" + +audit_add_watch will create a watch rule in the pointer to a pointer rulep. All that you need to pass it is the full path to a file and it will initialize the audit_rule_data structure for a watch. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR audit_add_rule_data (3), +.BR audit_delete_rule_data (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_delete_rule_data.3 b/framework/src/audit/docs/audit_delete_rule_data.3 new file mode 100644 index 00000000..20c8e131 --- /dev/null +++ b/framework/src/audit/docs/audit_delete_rule_data.3 @@ -0,0 +1,23 @@ +.TH "AUDIT_DELETE_RULE_DATA" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_delete_rule_data \- Delete audit rule +.SH "SYNOPSIS" +.B #include +.sp +int audit_delete_rule_data (int fd, struct audit_rule_data *rule, int flags, int action); + +.SH "DESCRIPTION" + +audit_delete_rule_data is used to delete rules that are currently loaded in the kernel. To delete a rule, you must set up the rules identical to the one being deleted. See audit_add_rule_data for flag and action definitions. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_add_rule_data (3), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_detect_machine.3 b/framework/src/audit/docs/audit_detect_machine.3 new file mode 100644 index 00000000..e6c55b36 --- /dev/null +++ b/framework/src/audit/docs/audit_detect_machine.3 @@ -0,0 +1,23 @@ +.TH "AUDIT_DETECT_MACHINE" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_detect_machine \- Detects the current machine type +.SH "SYNOPSIS" +.B #include +.sp +int audit_detect_machine (void); + +.SH "DESCRIPTION" + +audit_detect_machine queries uname and converts the kernel machine string to an enum value defined in machine_t. The machine type is needed for any use of the audit_name_to_syscall function. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, the return value is the machine's type. + +.SH "SEE ALSO" + +.BR uname (3), +.BR audit_name_to_syscall (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_encode_nv_string.3 b/framework/src/audit/docs/audit_encode_nv_string.3 new file mode 100644 index 00000000..3449786a --- /dev/null +++ b/framework/src/audit/docs/audit_encode_nv_string.3 @@ -0,0 +1,26 @@ +.TH "AUDIT_ENCODE_NV_STRING" "3" "Oct 2010" "Red Hat" "Linux Audit API" +.SH NAME +audit_encode_nv_string \- encode a name/value pair in a string +.SH SYNOPSIS +.B #include +.sp +.B char *audit_encode_nv_string(const char *name, const char *value, unsigned int vlen) + +.SH DESCRIPTION +This function is used to encode a name/value pair. This should be used on any field being logged that potentially contains a space, a double-quote, or a control character. Any value containing those have to be specially encoded for the auparse library to correctly handle the value. The encoding method is designed to prevent log injection attacks where malicious values could cause parsing errors. + +To use this function, pass the name string and value strings on their respective arguments. If the value is likely to have a NUL value embedded within it, you will need to pass a value length that tells in bytes how big the value is. Otherwise, you can pass a 0 for vlen and the function will simply use strlen against the value pointer. Also be aware that the name of the field will cause auparse to do certain things when interpretting the value. If the name is uid, a user id value in decimal is expected. Make sure that well known names are used for their intended purpose or that there is no chance of name collision with something new. + +.SH "RETURN VALUE" + +Returns a freshly malloc'ed string that the caller must free or NULL on error. + +.SH "SEE ALSO" + +.BR audit_log_user_message (3), +.BR audit_log_user_comm_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_semanage_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_get_reply.3 b/framework/src/audit/docs/audit_get_reply.3 new file mode 100644 index 00000000..da3e4c8e --- /dev/null +++ b/framework/src/audit/docs/audit_get_reply.3 @@ -0,0 +1,21 @@ +.TH "AUDIT_GET_REPLY" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_get_reply \- Get the audit system's reply +.SH SYNOPSIS +.B #include +.sp +int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek); + +.SH "DESCRIPTION" +This function gets the next data packet sent on the audit netlink socket. This function is usually called after sending a command to the audit system. fd should be an open file descriptor returned by audit_open. rep should be a data structure to put the reply in. block is of type reply_t which is either: GET_REPLY_BLOCKING and GET_REPLY_NONBLOCKING. peek, if non-zero, gets the data without dequeueing it from the netlink socket. + +.SH "RETURN VALUE" + +This function returns \-1 on error, 0 if error response received, and positive value on success. + +.SH "SEE ALSO" + +.BR audit_open (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_getloginuid.3 b/framework/src/audit/docs/audit_getloginuid.3 new file mode 100644 index 00000000..6a2b4ee8 --- /dev/null +++ b/framework/src/audit/docs/audit_getloginuid.3 @@ -0,0 +1,25 @@ +.TH "AUDIT_GETLOGINUID" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_getloginuid \- Get a program's loginuid value +.SH SYNOPSIS +.B #include +.sp +uid_t audit_getloginuid(void); + +.SH DESCRIPTION +This function returns the task attribute loginuid. + +.SH "RETURN VALUE" + +This function returns the loginuid value if it was set. It will return a \-1 if loginuid was unset. However, since uid_t is an unsigned type, you will see the converted value instead of \-1. + +.SH "ERRORS" + +This function returns \-1 on failure. However, in the event of a real error, errno would be set. The function can set errno based on failures of open, read, or strtoul. + +.SH "SEE ALSO" + +.BR audit_setloginuid (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_log_acct_message.3 b/framework/src/audit/docs/audit_log_acct_message.3 new file mode 100644 index 00000000..2ea6289b --- /dev/null +++ b/framework/src/audit/docs/audit_log_acct_message.3 @@ -0,0 +1,44 @@ +.TH "AUDIT_LOG_ACCT_MESSAGE" "3" "Oct 2010" "Red Hat" "Linux Audit API" +.SH NAME +audit_log_acct_message \- log a user account message +.SH SYNOPSIS +.B #include +.sp +.B int audit_log_acct_message(int audit_fd, int type, const char *pgname, +const char *op, const char *name, unsigned int id, const char *host, +const char *addr, const char *tty, int result) + +.SH DESCRIPTION +This function will log a message to the audit system using a predefined message format. It should be used for all account manipulation operations. The function +parameters are as follows: + +.nf +audit_fd - The fd returned by audit_open +type - type of message: AUDIT_USER_CHAUTHTOK for changing any account attributes. +pgname - program's name, if NULL will attempt to figure out +op - operation. Ex: "adding user", "changing finger info", "deleting group" +name - user's account or group name. If not available use NULL. +id - uid or gid that the operation is being performed on. If the user is unknown, pass a \-1 and fill in the name parameter. This is used only when user is NULL. +host - The hostname if known. If not available pass a NULL. +addr - The network address of the user. If not available pass a NULL. +tty - The tty of the user, if NULL will attempt to figure out +result - 1 is "success" and 0 is "failed" +.fi + +.SH "RETURN VALUE" + +It returns the sequence number which is > 0 on success or <= 0 on error. + +.SH "ERRORS" + +This function returns \-1 on failure. Examine errno for more info. + +.SH "SEE ALSO" + +.BR audit_log_user_message (3), +.BR audit_log_user_comm_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_semanage_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_log_semanage_message.3 b/framework/src/audit/docs/audit_log_semanage_message.3 new file mode 100644 index 00000000..7a6a6849 --- /dev/null +++ b/framework/src/audit/docs/audit_log_semanage_message.3 @@ -0,0 +1,53 @@ +.TH "AUDIT_LOG_SEMANAGE_MESSAGE" "3" "Jan 2012" "Red Hat" "Linux Audit API" +.SH NAME +audit_log_semanage_message \- log a semanage message +.SH SYNOPSIS +.B #include +.sp +.B int audit_log_semanage_message(int audit_fd, int type, +.B const char *pgname, const char *op, const char *name, unsigned int id, +.B const char *new_seuser, const char *new_role, const char *new_range, +.B const char *old_seuser, const char *old_role, const char *old_range, +.B const char *host, const char *addr, const char *tty, int result) + +.SH DESCRIPTION + +This function will log a message to the audit system using a predefined +message format. It should be used for all SE linux user and role +manipulation operations. The function parameters are as follows: + +.nf +audit_fd - The fd returned by audit_open +type - type of message: AUDIT_ROLE_ASSIGN/REMOVE for changing any SE Linux user or role attributes. +pgname - program's name +op - operation. "adding-user", "adding-role", "deleting-user", "deleting-role" +name - user's account. If not available use NULL. +id - uid that the operation is being performed on. This is used only when name is NULL. +new_seuser - the new seuser that the login user is getting +new_role - the new_role that the login user is getting +new_range - the new mls range that the login user is getting +old_seuser - the old seuser that the login usr had +old_role - the old role that the login user had +old_range - the old mls range that the login usr had +host - The hostname if known +addr - The network address of the user +tty - The tty of the user +result - 1 is "success" and 0 is "failed" +.fi + +.SH "RETURN VALUE" + +It returns the sequence number which is > 0 on success or <= 0 on error. + +.SH "ERRORS" + +This function returns \-1 on failure. Examine errno for more info. + +.SH "SEE ALSO" +.BR audit_log_user_message (3), +.BR audit_log_acct_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_user_comm_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_log_user_avc_message.3 b/framework/src/audit/docs/audit_log_user_avc_message.3 new file mode 100644 index 00000000..1a101950 --- /dev/null +++ b/framework/src/audit/docs/audit_log_user_avc_message.3 @@ -0,0 +1,40 @@ +.TH "AUDIT_LOG_USER_AVC_MESSAGE" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_log_user_avc_message \- log a user avc message +.SH SYNOPSIS +.B #include +.sp +.B int audit_log_user_avc_message(int audit_fd, int type, const char *message, +const char *hostname, const char *addr, const char *tty, uid_t uid) + +.SH DESCRIPTION + +This function will log a message to the audit system using a predefined message format. This function should be used by all apps that are SE Linux object managers. The function parameters are as follows: + +.nf +audit_fd - The fd returned by audit_open +type - type of message, ex: AUDIT_USER_AVC +message - the message being sent +hostname - the hostname if known +addr - The network address of the user +tty - The tty of the user, if NULL will attempt to figure out +uid - The auid of the person related to the avc message +.fi + +.SH "RETURN VALUE" + +It returns the sequence number which is > 0 on success or <= 0 on error. + +.SH "ERRORS" + +This function returns \-1 on failure. Examine errno for more info. + +.SH "SEE ALSO" + +.BR audit_log_user_message (3), +.BR audit_log_acct_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_semanage_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_log_user_comm_message.3 b/framework/src/audit/docs/audit_log_user_comm_message.3 new file mode 100644 index 00000000..fb4912d9 --- /dev/null +++ b/framework/src/audit/docs/audit_log_user_comm_message.3 @@ -0,0 +1,45 @@ +.TH "AUDIT_LOG_USER_COMM_MESSAGE" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_log_user_comm_message \- log a user message from a console app +.SH SYNOPSIS +.B #include +.sp +.B int audit_log_user_comm_message(int audit_fd, int type, const char *message, +const char *comm, const char *hostname, const char *addr, const char *tty, +int result) + +.SH DESCRIPTION +This function will log a message to the audit system using a predefined +message format. This function should be used by all console apps that do +not manipulate accounts or groups and are executing a script. An example +would be python or crond wanting to say what they are executing. The function +parameters are as follows: + +.nf +audit_fd - The fd returned by audit_open +type - type of message, ex: AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN +message - the message text being sent +comm - the program command line name +hostname - the hostname if known, NULL if unknown +addr - The network address of the user, NULL if unknown +tty - The tty of the user, if NULL will attempt to figure out +result - 1 is "success" and 0 is "failed" +.fi + +.SH "RETURN VALUE" + +It returns the sequence number which is > 0 on success or <= 0 on error. + +.SH "ERRORS" + +This function returns \-1 on failure. Examine errno for more info. + +.SH "SEE ALSO" + +.BR audit_log_user_message (3), +.BR audit_log_acct_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_semanage_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_log_user_command.3 b/framework/src/audit/docs/audit_log_user_command.3 new file mode 100644 index 00000000..39e67560 --- /dev/null +++ b/framework/src/audit/docs/audit_log_user_command.3 @@ -0,0 +1,37 @@ +.TH "AUDIT_LOG_USER_COMMAND" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +audit_log_user_command \- log a user command +.SH SYNOPSIS +.B #include +.sp +.B int audit_log_user_command(int audit_fd, int type, const char *command, const char *tty, int result); + +.SH DESCRIPTION +This function will log a command to the audit system using a predefined message format. It encodes the command as the audit system expects for untrusted strings. This function should be used by all apps need to record commands. The function parameters are as follows: + +.nf +audit_fd - The fd returned by audit_open +type - type of message, ex: AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN +command - the command being logged +tty - The tty of the user, if NULL will attempt to figure out +result - 1 is "success" and 0 is "failed" +.fi + +.SH "RETURN VALUE" + +It returns the sequence number which is > 0 on success or <= 0 on error. + +.SH "ERRORS" + +This function returns \-1 on failure. Examine errno for more info. + +.SH "SEE ALSO" + +.BR audit_log_user_message (3), +.BR audit_log_user_comm_message (3), +.BR audit_log_acct_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_semanage_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_log_user_message.3 b/framework/src/audit/docs/audit_log_user_message.3 new file mode 100644 index 00000000..2954c400 --- /dev/null +++ b/framework/src/audit/docs/audit_log_user_message.3 @@ -0,0 +1,42 @@ +.TH "AUDIT_LOG_USER_MESSAGE" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_log_user_message \- log a general user message +.SH SYNOPSIS +.B #include +.sp +.B int audit_log_user_message(int audit_fd, int type, const char *message, +const char *hostname, const char *addr, const char *tty, +int result) + +.SH DESCRIPTION +This function will log a message to the audit system using a predefined +message format. This function should be used by all console apps that do +not manipulate accounts or groups. The function parameters are as follows: + +.nf +audit_fd - The fd returned by audit_open +type - type of message, ex: AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN +message - the message text being sent +hostname - the hostname if known, NULL if unknown +addr - The network address of the user, NULL if unknown +tty - The tty of the user, if NULL will attempt to figure out +result - 1 is "success" and 0 is "failed" +.fi + +.SH "RETURN VALUE" + +It returns the sequence number which is > 0 on success or <= 0 on error. + +.SH "ERRORS" + +This function returns \-1 on failure. Examine errno for more info. + +.SH "SEE ALSO" + +.BR audit_log_user_comm_message (3), +.BR audit_log_acct_message (3), +.BR audit_log_user_avc_message (3), +.BR audit_log_semanage_message (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_open.3 b/framework/src/audit/docs/audit_open.3 new file mode 100644 index 00000000..6ec8eb0a --- /dev/null +++ b/framework/src/audit/docs/audit_open.3 @@ -0,0 +1,34 @@ +.TH "AUDIT_OPEN" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_open \- Open a audit netlink socket connection +.SH "SYNOPSIS" +.B #include +.sp +int audit_open (void); + +.SH "DESCRIPTION" + +audit_open creates a NETLINK_AUDIT socket for communication with the kernel part of the Linux Audit Subsystem. The audit system uses the ACK feature of netlink. This means that every message to the kernel will return a netlink status packet even if the operation succeeds. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, the return value is a descriptor referencing the socket. + +.SH ERRORS + +The +.BR audit_open () +function may fail and set +.I errno +for any of the errors specified for the +.BR socket (2) +and +.BR fcntl (2) +routines. + +.SH "SEE ALSO" + +.BR netlink (7). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_request_rules_list_data.3 b/framework/src/audit/docs/audit_request_rules_list_data.3 new file mode 100644 index 00000000..f524ea90 --- /dev/null +++ b/framework/src/audit/docs/audit_request_rules_list_data.3 @@ -0,0 +1,25 @@ +.TH "AUDIT_REQUEST_LIST_DATA" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_request_rules_list_data \- Request list of current audit rules +.SH "SYNOPSIS" +.B #include +.sp +int audit_request_rules_list_data (int fd); + +.SH "DESCRIPTION" + +audit_request_rules_list_data sends a request to the kernel to list the current audit rules. The rules are sent back one after another after this request is issued. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_add_rule_data (3), +.BR audit_delete_rule_data (3), +.BR audit_open (3), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_request_signal_info.3 b/framework/src/audit/docs/audit_request_signal_info.3 new file mode 100644 index 00000000..873deb58 --- /dev/null +++ b/framework/src/audit/docs/audit_request_signal_info.3 @@ -0,0 +1,33 @@ +.TH "AUDIT_" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +audit_request_signal_info \- Request signal info for the audit system +.SH "SYNOPSIS" +.B #include +.sp +int audit_request_signal_info(int fd); + +.SH "DESCRIPTION" + +audit_request_signal_info requests that the kernel send information about the sender of a signal to the audit daemon. The sinal info structure is as follows: + +.nf +struct audit_sig_info { + uid_t uid; + pid_t pid; + char ctx[0]; +}; +.fi + +This function is likely to be used only by audit daemons and shouldn't be called by any other kind of program. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_open (3), +.BR audit_get_reply (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_request_status.3 b/framework/src/audit/docs/audit_request_status.3 new file mode 100644 index 00000000..bb872196 --- /dev/null +++ b/framework/src/audit/docs/audit_request_status.3 @@ -0,0 +1,44 @@ +.TH "AUDIT_REQUEST_STATUS" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_request_status \- Request status of the audit system +.SH "SYNOPSIS" + +.B #include +.sp +int audit_request_status (int fd); + +.SH "DESCRIPTION" + +.PP +audit_request_status requests that the kernel send status structure describing various settings. The audit_status structure is as follows: + +.RS +.ta 4n 10n 24n +.nf + +struct audit_status { + __u32 mask; /* Bit mask for valid entries */ + __u32 enabled; /* 1 = enabled, 0 = disabled */ + __u32 failure; /* Failure-to-log action */ + __u32 pid; /* pid of auditd process */ + __u32 rate_limit; /* messages rate limit (per second) */ + __u32 backlog_limit; /* waiting messages limit */ + __u32 lost; /* messages lost */ + __u32 backlog; /* messages waiting in queue */ +}; +.fi +.ta +.RE + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_open (3), +.BR audit_get_reply (3), +.BR auditd (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_set_backlog_limit.3 b/framework/src/audit/docs/audit_set_backlog_limit.3 new file mode 100644 index 00000000..18c52448 --- /dev/null +++ b/framework/src/audit/docs/audit_set_backlog_limit.3 @@ -0,0 +1,26 @@ +.TH "AUDIT_SET_BACKLOG_LIMIT" "3" "Oct 2006" "Linux Audit API" +.SH NAME +audit_set_backlog_limit \- Set the audit backlog limit +.SH "SYNOPSIS" + +.B #include +.sp +int audit_set_backlog_limit (int fd, int limit); + +.SH "DESCRIPTION" + +audit_set_backlog_limit sets the queue length for audit events awaiting transfer to the audit daemon. The default value is 64 which can potentially be overrun by bursts of activity. When the backlog limit is reached, the kernel consults the failure_flag to see what action to take. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_set_failure (3), +.BR audit_open (3), +.BR auditd (8), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_set_backlog_wait_time.3 b/framework/src/audit/docs/audit_set_backlog_wait_time.3 new file mode 100644 index 00000000..4a7b9aee --- /dev/null +++ b/framework/src/audit/docs/audit_set_backlog_wait_time.3 @@ -0,0 +1,26 @@ +.TH "AUDIT_SET_BACKLOG_WAIT_TIME" "3" "Oct 2014" "Linux Audit API" +.SH NAME +audit_set_backlog_wait_time \- Set the audit backlog wait time +.SH "SYNOPSIS" + +.B #include +.sp +int audit_set_backlog_wait_time (int fd, int wait_time); + +.SH "DESCRIPTION" + +audit_set_backlog_wait_time sets the time that the kernel will wait before attempting to send more audit events to be transferred to the audit daemon when the backlog_limit is reached. This gives the audit daemon a chance to drain the kernel queue. The default value is 60000 or 60 * HZ setting in the kernel. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_set_backlog_limit (3), +.BR audit_open (3), +.BR auditd (8), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_set_enabled.3 b/framework/src/audit/docs/audit_set_enabled.3 new file mode 100644 index 00000000..331f1ce6 --- /dev/null +++ b/framework/src/audit/docs/audit_set_enabled.3 @@ -0,0 +1,27 @@ +.TH "AUDIT_SET_ENABLED" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_set_enabled \- Enable or disable auditing +.SH "SYNOPSIS" + +.B #include +.sp +int audit_set_enabled (int fd, int enabled); + +.SH "DESCRIPTION" + +.PP +audit_set_enabled is used to control whether or not the audit system is active. When the audit system is enabled (enabled set to 1), every syscall will pass through the audit system to collect information and potentially trigger an event. + +If the audit system is disabled (enabled set to 0), syscalls do not enter the audit system and no data is collected. There may be some events generated by MAC subsystems like SE Linux even though the audit system is disabled. It is possible to suppress those events, too, by adding an audit rule with flags set to AUDIT_FILTER_TYPE. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_add_rule_data (3), +.BR auditd (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_set_failure.3 b/framework/src/audit/docs/audit_set_failure.3 new file mode 100644 index 00000000..cf526f03 --- /dev/null +++ b/framework/src/audit/docs/audit_set_failure.3 @@ -0,0 +1,38 @@ +.TH "AUDIT_SET_FAILURE" "3" "June 2015" "Red Hat" "Linux Audit API" +.SH NAME +audit_set_failure \- Set audit failure flag +.SH "SYNOPSIS" + +.B #include +.sp +int audit_set_failure(int fd, int failure); + +.SH "DESCRIPTION" + +audit_set_failure sets the action that the kernel will perform when the backlog limit is reached or when it encounters an error and cannot proceed. Possible values are: + +.TP +0 - AUDIT_FAIL_SILENT +Do nothing, report nothing, skip logging the record and continue. + +.TP +1 - AUDIT_FAIL_PRINTK [default] +Log the audit record using printk which will cause subsequent events to get written to syslog. + +.TP +2 - AUDIT_FAIL_PANIC +Call the panic function. This would be used to prevent use of the machine upon loss of audit events. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_set_backlog (3), +.BR audit_open (3), +.BR auditd (8), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_set_pid.3 b/framework/src/audit/docs/audit_set_pid.3 new file mode 100644 index 00000000..d2b33db8 --- /dev/null +++ b/framework/src/audit/docs/audit_set_pid.3 @@ -0,0 +1,24 @@ +.TH "AUDIT_SET_PID" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_set_pid \- Set audit daemon process ID +.SH "SYNOPSIS" + +.B #include +.sp +int audit_set_pid (int fd, int pid); + +.SH "DESCRIPTION" + +audit_set_pid tells the kernel what the pid is of the audit daemon. When the pid is set to 0, the kernel will log all events to syslog. Otherwise it will try to send events to the netlink connection that has the same pid given by this function. If for some reason the process goes away, the kernel will automatically set the value to 0 itself. Usually this function is called by the audit daemon and not an external program. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_open (3), +.BR auditd (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_set_rate_limit.3 b/framework/src/audit/docs/audit_set_rate_limit.3 new file mode 100644 index 00000000..90300eaf --- /dev/null +++ b/framework/src/audit/docs/audit_set_rate_limit.3 @@ -0,0 +1,24 @@ +.TH "AUDIT_SET_RATE_LIMIT" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_set_rate_limit \- Set audit rate limit +.SH "SYNOPSIS" + +.B #include +.sp +int audit_set_rate_limit (int fd, int limit); + +.SH "DESCRIPTION" + +audit_set_rate_limit will set the maximum number of messages that the kernel will send per second. This can be used to throttle the rate if systems become unresponsive. Of course the trade off is that events will be dropped. The default value is 0, meaning no limit. + +.SH "RETURN VALUE" + +The return value is <= 0 on error, otherwise it is the netlink sequence id number. This function can have any error that sendto would encounter. + +.SH "SEE ALSO" + +.BR audit_open (3), +.BR auditd (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_setloginuid.3 b/framework/src/audit/docs/audit_setloginuid.3 new file mode 100644 index 00000000..c1a71e31 --- /dev/null +++ b/framework/src/audit/docs/audit_setloginuid.3 @@ -0,0 +1,25 @@ +.TH "AUDIT_SETLOGINUID" "3" "Oct 2006" "Red Hat" "Linux Audit API" +.SH NAME +audit_setloginuid \- Set a program's loginuid value +.SH SYNOPSIS +.B #include +.sp +int audit_setloginuid(uid_t uid); + +.SH "DESCRIPTION" + +This function sets the task attribute loginuid with the value of uid. The loginuid value may only be set by programs with the CAP_AUDIT_CONTROL capability. This normally means the root account. +.sp +The loginuid value is part of the task structure and is inheritted by child processes. It is used to track what account a user gained system access with. All system entry point programs should set this value right before changing to the uid of the user granted access so that audit events are properly attributed to the that user. + +.SH "RETURN VALUE" + +This function returns 0 on success and non-zero otherwise. + +.SH "SEE ALSO" + +.BR audit_getloginuid (3), +.BR pam_loginuid (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/audit_update_watch_perms.3 b/framework/src/audit/docs/audit_update_watch_perms.3 new file mode 100644 index 00000000..5b1e9ee9 --- /dev/null +++ b/framework/src/audit/docs/audit_update_watch_perms.3 @@ -0,0 +1,23 @@ +.TH "AUDIT_UPDATE_WATCH_PERMS" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +audit_update_watch_perms \- update permissions field of watch command +.SH "SYNOPSIS" +.B #include +.sp +int audit_update_watch_perms(struct audit_rule_data *rule, int perms); + +.SH "DESCRIPTION" + +audit_update_watch_perms adds the permission checks to a watch command that is being built. The perms are a bitwise or'ing of: AUDIT_PERM_EXEC, AUDIT_PERM_WRITE, AUDIT_PERM_READ, AUDIT_PERM_ATTR. + +.SH "RETURN VALUE" + +Returns a number < 0 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR audit_add_rule_data (3), +.BR audit_add_watch (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auditctl.8 b/framework/src/audit/docs/auditctl.8 new file mode 100644 index 00000000..ceb6c40b --- /dev/null +++ b/framework/src/audit/docs/auditctl.8 @@ -0,0 +1,315 @@ +.TH AUDITCTL: "8" "Aug 2014" "Red Hat" "System Administration Utilities" +.SH NAME +auditctl \- a utility to assist controlling the kernel's audit system +.SH SYNOPSIS +\fBauditctl\fP [\fIoptions\fP] +.SH DESCRIPTION +The \fBauditctl\fP program is used to configure kernel options related to auditing, to see status of the configuration, and to load discretionary audit rules. +.SH CONFIGURATION OPTIONS +.TP +.BI \-b\ backlog +Set max number of outstanding audit buffers allowed (Kernel Default=64) If all buffers are full, the failure flag is consulted by the kernel for action. +.TP +.BI \-\-backlog_wait_time \ \fIwait_time\fP +Set the time for the kernel to wait (Kernel Default 60*HZ) when the backlog_limit is reached before queuing more audit events to be transferred to auditd. The number must be greater than or equal to zero and less that 10 times the default value. +.TP +.B \-c +Continue loading rules in spite of an error. This summarizes the results of loading the rules. The exit code will not be success if any rule fails to load. +.TP +.B \-D +Delete all rules and watches. This can take a key option (\-k), too. +.TP +\fB\-e\fP [\fB0\fP..\fB2\fP] +Set enabled flag. When \fB0\fP is passed, this can be used to temporarily disable auditing. When \fB1\fP is passed as an argument, it will enable auditing. To lock the audit configuration so that it can't be changed, pass a \fB2\fP as the argument. Locking the configuration is intended to be the last command in audit.rules for anyone wishing this feature to be active. Any attempt to change the configuration in this mode will be audited and denied. The configuration can only be changed by rebooting the machine. +.TP +\fB\-f\fP [\fB0\fP..\fB2\fP] +Set failure mode +\fB0\fP=silent \fB1\fP=printk \fB2\fP=panic. This option lets you determine how you want the kernel to handle critical errors. Example conditions where this mode may have an effect includes: transmission errors to userspace audit daemon, backlog limit exceeded, out of kernel memory, and rate limit exceeded. The default value is \fB1\fP. Secure environments will probably want to set this to \fB2\fP. +.TP +.B \-h +Help +.TP +.B \-i +Ignore errors when reading rules from a file. This causes auditctl to always return a success exit code. +.TP +.BI \-\-loginuid-immutable +This option tells the kernel to make loginuids unchangeable once they are set. Changing loginuids requires CAP_AUDIT_CONTROL. So, its not something that can be done by unprivileged users. Setting this makes loginuid tamper-proof, but can cause some problems in certain kinds of containers. +.TP +.BI \-q\ mount-point,subtree +If you have an existing directory watch and bind or move mount another subtree in the watched subtree, you need to tell the kernel to make the subtree being mounted equivalent to the directory being watched. If the subtree is already mounted at the time the directory watch is issued, the subtree is automatically tagged for watching. Please note the comma separating the two values. Omitting it will cause errors. +.TP +.BI \-r\ rate +Set limit in messages/sec (\fB0\fP=none). If this \fIrate\fP is non-zero and is exceeded, the failure flag is consulted by the kernel for action. The default value is \fB0\fP. +.TP +.BI \-R\ file +Read rules from a \fIfile\fP. The rules must be 1 per line and in the order that they are to be executed in. The rule file must be owned by root and not readable by other users or it will be rejected. The rule file may have comments embedded by starting the line with a '#' character. Rules that are read from a file are identical to what you would type on a command line except they are not preceded by auditctl (since auditctl is the one executing the file) and you would not use shell escaping since auditctl is reading the file instead of bash. +.TP +.BI \-t +Trim the subtrees after a mount command. +.SH STATUS OPTIONS +.TP +.B \-l +List all rules 1 per line. Two more options may be given to this command. You can give either a key option (\-k) to list rules that match a key or a (\-i) to have a0 through a3 interpretted to help determine the syscall argument values are correct . +.TP +.BI \-m\ text +Send a user space message into the audit system. This can only be done if you have CAP_AUDIT_WRITE capability (normally the root user has this). The resulting event will be the USER type. +.TP +.B \-s +Report the kernel's audit subsystem status. It will tell you the in-kernel values that can be set by \fB-e\fP, \fB-f\fP, \fB-r\fP, and \fB-b\fP options. The pid value is the process number of the audit daemon. Note that a pid of 0 indicates that the audit daemon is not running. The lost entry will tell you how many event records that have been discarded due to the kernel audit queue overflowing. The backlog field tells how many event records are currently queued waiting for auditd to read them. This option can be followed by the \fB-i\fP to get a couple fields interpreted. +.TP +.BI \-v +Print the version of auditctl. + +.SH RULE OPTIONS +.TP +.BI \-a\ [ list,action | action,list ] +Append rule to the end of \fIlist\fP with \fIaction\fP. Please note the comma separating the two values. Omitting it will cause errors. The fields may be in either order. It could be list,action or action,list. The following describes the valid \fIlist\fP names: +.RS +.TP 12 +.B task +Add a rule to the per task list. This rule list is used only at the time a task is created -- when fork() or clone() are called by the parent task. When using this list, you should only use fields that are known at task creation time, such as the uid, gid, etc. +.TP +.B exit +Add a rule to the syscall exit list. This list is used upon exit from a system call to determine if an audit event should be created. +.TP +.B user +Add a rule to the user message filter list. This list is used by the kernel to filter events originating in user space before relaying them to the audit daemon. It should be noted that the only fields that are valid are: uid, auid, gid, pid, subj_user, subj_role, subj_type, subj_sen, subj_clr, and msgtype. All other fields will be treated as non-matching. It should be understood that any event originating from user space from a process that has CAP_AUDIT_WRITE will be recorded into the audit trail. This means that the most likely use for this filter is with rules that have an action of never since nothing has to be done to allow events to be recorded. +.TP +.B exclude +Add a rule to the event type exclusion filter list. This list is used to filter events that you do not want to see. For example, if you do not want to see any avc messages, you would using this list to record that. The message type that you do not wish to see is given with the msgtype field. +.RE + +The following describes the valid \fIactions\fP for the rule: +.RS +.TP 12 +.B never +No audit records will be generated. This can be used to suppress event generation. In general, you want suppressions at the top of the list instead of the bottom. This is because the event triggers on the first matching rule. +.TP +.B always +Allocate an audit context, always fill it in at syscall entry time, and always write out a record at syscall exit time. +.RE +.TP +.BI \-A\ list , action +Add rule to the beginning \fIlist\fP with \fIaction\fP. +.TP +\fB\-C\fP [\fIf\fP\fB=\fP\fIf\fP | \fIf\fP\fB!=\fP\fIf\fP] +Build an inter-field comparison rule: field, operation, field. You may pass multiple comparisons on a single command line. Each one must start with \fB\-C\fP. Each inter-field equation is anded with each other as well as equations starting with \fB\-F\fP to trigger an audit record. There are 2 operators supported - equal, and not equal. Valid fields are: +.RS +.TP 12 +.B auid, uid, euid, suid, fsuid, obj_uid; and gid, egid, sgid, fsgid, obj_gid +.RE + +.RS +The two groups of uid and gid cannot be mixed. But any comparison within the group can be made. The obj_uid/gid fields are collected from the object of the event such as a file or directory. +.RE + +.TP +.BI \-d\ list , action +Delete rule from \fIlist\fP with \fIaction\fP. The rule is deleted only if it exactly matches syscall name(s) and every field name and value. +.TP +\fB\-F\fP [\fIn\fP\fB=\fP\fIv\fP | \fIn\fP\fB!=\fP\fIv\fP | \fIn\fP\fB<\fP\fIv\fP | \fIn\fP\fB>\fP\fIv\fP | \fIn\fP\fB<=\fP\fIv\fP | \fIn\fP\fB>=\fP\fIv\fP | \fIn\fP\fB&\fP\fIv\fP | \fIn\fP\fB&=\fP\fIv\fP] +Build a rule field: name, operation, value. You may have up to 64 fields passed on a single command line. Each one must start with \fB\-F\fP. Each field equation is anded with each other (as well as equations starting with \fB\-C\fP) to trigger an audit record. There are 8 operators supported - equal, not equal, less than, greater than, less than or equal, and greater than or equal, bit mask, and bit test respectively. Bit test will "and" the values and check that they are equal, bit mask just "ands" the values. Fields that take a user ID may instead have the user's name; the program will convert the name to user ID. The same is true of group names. Valid fields are: +.RS +.TP 12 +.B a0, a1, a2, a3 +Respectively, the first 4 arguments to a syscall. Note that string arguments are not supported. This is because the kernel is passed a pointer to the string. Triggering on a pointer address value is not likely to work. So, when using this, you should only use on numeric values. This is most likely to be used on platforms that multiplex socket or IPC operations. +.TP +.B arch +The CPU architecture of the syscall. The arch can be found doing 'uname \-m'. If you do not know the arch of your machine but you want to use the 32 bit syscall table and your machine supports 32 bit, you can also use +.B b32 +for the arch. The same applies to the 64 bit syscall table, you can use +.B b64. +In this way, you can write rules that are somewhat arch independent because the family type will be auto detected. However, syscalls can be arch specific and what is available on x86_64, may not be available on ppc. The arch directive should precede the \-S option so that auditctl knows which internal table to use to look up the syscall numbers. +.TP +.B auid +The original ID the user logged in with. Its an abbreviation of audit uid. Sometimes its referred to as loginuid. Either the user account text or number may be used. +.TP +.B devmajor +Device Major Number +.TP +.B devminor +Device Minor Number +.TP +.B dir +Full Path of Directory to watch. This will place a recursive watch on the directory and its whole subtree. It can only be used on exit list. See "\fB\-w\fP". +.TP +.B egid +Effective Group ID. May be numeric or the groups name. +.TP +.B euid +Effective User ID. May be numeric or the user account name. +.TP +.B exit +Exit value from a syscall. If the exit code is an errno, you may use the text representation, too. +.TP +.B fsgid +Filesystem Group ID. May be numeric or the groups name. +.TP +.B fsuid +Filesystem User ID. May be numeric or the user account name. +.TP +.B filetype +The target file's type. Can be either file, dir, socket, link, character, block, or fifo. +.TP +.B gid +Group ID. May be numeric or the groups name. +.TP +.B inode +Inode Number +.TP +.B key +This is another way of setting a filter key. See discussion above for \fB\-k\fP option. +.TP +.B msgtype +This is used to match the event's record type. It should only be used on the exclude or user filter lists. +.TP +.B obj_uid +Object's UID +.TP +.B obj_gid +Object's GID +.TP +.B obj_user +Resource's SE Linux User +.TP +.B obj_role +Resource's SE Linux Role +.TP +.B obj_type +Resource's SE Linux Type +.TP +.B obj_lev_low +Resource's SE Linux Low Level +.TP +.B obj_lev_high +Resource's SE Linux High Level +.TP +.B path +Full Path of File to watch. It can only be used on exit list. +.TP +.B perm +Permission filter for file operations. See "\fB\-p\fP". It can only be used on exit list. You can use this without specifying a syscall and the kernel will select the syscalls that satisfy the permissions being requested. +.TP +.B pers +OS Personality Number +.TP +.B pid +Process ID +.TP +.B ppid +Parent's Process ID +.TP +.B subj_user +Program's SE Linux User +.TP +.B subj_role +Program's SE Linux Role +.TP +.B subj_type +Program's SE Linux Type +.TP +.B subj_sen +Program's SE Linux Sensitivity +.TP +.B subj_clr +Program's SE Linux Clearance +.TP +.B sgid +Saved Group ID. See getresgid(2) man page. +.TP +.B success +If the exit value is >= 0 this is true/yes otherwise its false/no. When writing a rule, use a 1 for true/yes and a 0 for false/no +.TP +.B suid +Saved User ID. See getresuid(2) man page. +.TP +.B uid +User ID. May be numeric or the user account name. +.RE +.TP +.BI \-k\ key +Set a filter key on an audit rule. The filter key is an arbitrary string of text that can be up to 31 bytes long. It can uniquely identify the audit records produced by a rule. Typical use is for when you have several rules that together satisfy a security requirement. The key value can be searched on with ausearch so that no matter which rule triggered the event, you can find its results. The key can also be used on delete all (\-D) and list rules (\-l) to select rules with a specific key. You may have more than one key on a rule if you want to be able to search logged events in multiple ways or if you have an audispd plugin that uses a key to aid its analysis. +.TP +\fB\-p\fP [\fBr\fP|\fBw\fP|\fBx\fP|\fBa\fP] +Describe the permission access type that a file system watch will trigger on. \fBr\fP=read, \fBw\fP=write, \fBx\fP=execute, \fBa\fP=attribute change. These permissions are not the standard file permissions, but rather the kind of syscall that would do this kind of thing. The read & write syscalls are omitted from this set since they would overwhelm the logs. But rather for reads or writes, the open flags are looked at to see what permission was requested. +.TP +\fB\-S\fP [\fISyscall name or number\fP|\fBall\fP] +Any \fIsyscall name\fP or \fInumber\fP may be used. The word '\fBall\fP' may also be used. If the given syscall is made by a program, then start an audit record. If a field rule is given and no syscall is specified, it will default to all syscalls. You may also specify multiple syscalls in the same rule by using multiple \-S options in the same rule. Doing so improves performance since fewer rules need to be evaluated. Alternatively, you may pass a comma separated list of syscall names. If you are on a bi-arch system, like x86_64, you should be aware that auditctl simply takes the text, looks it up for the native arch (in this case b64) and sends that rule to the kernel. If there are no additional arch directives, IT WILL APPLY TO BOTH 32 & 64 BIT SYSCALLS. This can have undesirable effects since there is no guarantee that any syscall has the same number on both 32 and 64 bit interfaces. You will likely want to control this and write 2 rules, one with arch equal to b32 and one with b64 to make sure the kernel finds the events that you intend. See the arch field discussion for more info. +.TP +.BI \-w\ path +Insert a watch for the file system object at \fIpath\fP. You cannot insert a watch to the top level directory. This is prohibited by the kernel. Wildcards are not supported either and will generate a warning. The way that watches work is by tracking the inode internally. If you place a watch on a file, its the same as using the \-F path option on a syscall rule. If you place a watch on a directory, its the same as using the \-F dir option on a syscall rule. The \-w form of writing watches is for backwards compatibility and the syscall based form is more expressive. Unlike most syscall auditing rules, watches do not impact performance based on the number of rules sent to the kernel. The only valid options when using a watch are the \-p and \-k. If you need to anything fancy like audit a specific user accessing a file, then use the syscall auditing form with the path or dir fields. See the EXAMPLES section for an example of converting one form to another. +.TP +.BI \-W\ path +Remove a watch for the file system object at \fIpath\fP. The rule must match exactly. See \fB-d\fP discussion for more info. +.SH "PERFORMANCE TIPS" +Syscall rules get evaluated for each syscall for every program. If you have 10 syscall rules, every program on your system will delay during a syscall while the audit system evaluates each rule. Too many syscall rules will hurt performance. Try to combine as many as you can whenever the filter, action, key, and fields are identical. For example: + +.nf +.B auditctl \-a always,exit \-S openat \-F success=0 +.fi +.nf +.B auditctl \-a always,exit \-S truncate \-F success=0 +.fi + +could be re-written as one rule: + +.nf +.B auditctl \-a always,exit \-S openat \-S truncate \-F success=0 +.fi + +Also, try to use file system auditing wherever practical. This improves performance. For example, if you were wanting to capture all failed opens & truncates like above, but were only concerned about files in /etc and didn't care about /usr or /sbin, its possible to use this rule: + +.nf +.B auditctl \-a always,exit \-S openat \-S truncate \-F dir=/etc \-F success=0 +.fi + +This will be higher performance since the kernel will not evaluate it each and every syscall. It will be handled by the filesystem auditing code and only checked on filesystem related syscalls. +.SH "EXAMPLES" +To see all syscalls made by a specific program: + +.nf +.B auditctl \-a always,exit \-S all \-F pid=1005 +.fi + +To see files opened by a specific user: + +.nf +.B auditctl \-a always,exit \-S openat \-F auid=510 +.fi + +To see unsuccessful openat calls: + +.nf +.B auditctl \-a always,exit \-S openat \-F success=0 +.fi + +To watch a file for changes (2 ways to express): + +.nf +.B auditctl \-w /etc/shadow \-p wa +.B auditctl \-a always,exit \-F path=/etc/shadow \-F perm=wa +.fi + +To recursively watch a directory for changes (2 ways to express): + +.nf +.B auditctl \-w /etc/ \-p wa +.B auditctl \-a always,exit \-F dir=/etc/ \-F perm=wa +.fi + +To see if an admin is accessing other user's files: + +.nf +.B auditctl \-a always,exit \-F dir=/home/ \-F uid=0 \-C auid!=obj_uid +.fi + +.SH FILES +.TP +.I /etc/audit/audit.rules + +.SH "SEE ALSO" +.BR audit.rules (7), +.BR auditd (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auditd.8 b/framework/src/audit/docs/auditd.8 new file mode 100644 index 00000000..ed026439 --- /dev/null +++ b/framework/src/audit/docs/auditd.8 @@ -0,0 +1,74 @@ +.TH "AUDITD" "8" "Sept 2013" "Red Hat" "System Administration Utilities" +.SH NAME +auditd \- The Linux Audit daemon +.SH SYNOPSIS +.B auditd +.RB [ \-f ]\ [ \-l ]\ [ \-n ]\ [ \-s\ disable|enable|nochange ] +.SH DESCRIPTION +\fBauditd\fP is the userspace component to the Linux Auditing System. It's responsible for writing audit records to the disk. Viewing the logs is done with the +.B ausearch +or +.B aureport +utilities. Configuring the audit system or loading rules is done with the +.B auditctl +utility. During startup, the rules in \fI/etc/audit/audit.rules\fP are read by \fBauditctl\fP and loaded into the kernel. Alternately, there is also an +.B augenrules +program that reads rules located in \fI/etc/audit/rules.d/\fP and compiles them into an audit.rules file. The audit daemon itself has some configuration options that the admin may wish to customize. They are found in the +.B auditd.conf +file. +.SH OPTIONS +.TP +.B \-f +leave the audit daemon in the foreground for debugging. Messages also go to stderr rather than the audit log. +.TP +.B \-l +allow the audit daemon to follow symlinks for config files. +.TP +.B \-n +no fork. This is useful for running off of inittab or systemd. +.TP +.B \-s=\fIENABLE_STATE\fR +specify when starting if auditd should change the current value for the kernel enabled flag. Valid values for ENABLE_STATE are "disable", "enable" or "nochange". The default is to enable (and disable when auditd terminates). The value of the enabled flag may be changed during the lifetime of auditd using 'auditctl \-e'. +.SH SIGNALS +.TP +SIGHUP +causes auditd to reconfigure. This means that auditd re-reads the configuration file. If there are no syntax errors, it will proceed to implement the requested changes. If the reconfigure is successful, a DAEMON_CONFIG event is recorded in the logs. If not successful, error handling is controlled by space_left_action, admin_space_left_action, disk_full_action, and disk_error_action parameters in auditd.conf. + +.TP +SIGTERM +caused auditd to discontinue processing audit events, write a shutdown audit event, and exit. + +.TP +SIGUSR1 +causes auditd to immediately rotate the logs. It will consult the max_log_size_action to see if it should keep the logs or not. + +.TP +SIGUSR2 +causes auditd to attempt to resume logging. This is usually needed after logging has been suspended. + +.SH FILES +.B /etc/audit/auditd.conf +- configuration file for audit daemon +.P +.B /etc/audit/audit.rules +- audit rules to be loaded at startup +.P +.B /etc/audit/rules.d/ +- directory holding individual sets of rules to be compiled into one file by augenrules. + +.SH NOTES +A boot param of audit=1 should be added to ensure that all processes that run before the audit daemon starts is marked as auditable by the kernel. Not doing that will make a few processes impossible to properly audit. + +The audit daemon can receive audit events from other audit daemons via the audisp\-remote audispd plugin. The audit daemon may be linked with tcp_wrappers to control which machines can connect. If this is the case, you can add an entry to hosts.allow and deny. + +.SH "SEE ALSO" +.BR auditd.conf (5), +.BR audispd (8), +.BR ausearch (8), +.BR aureport (8), +.BR auditctl (8), +.BR augenrules (8), +.BR audit.rules (7). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auditd.conf.5 b/framework/src/audit/docs/auditd.conf.5 new file mode 100644 index 00000000..6bb6633c --- /dev/null +++ b/framework/src/audit/docs/auditd.conf.5 @@ -0,0 +1,304 @@ +.TH AUDITD.CONF: "5" "March 2014" "Red Hat" "System Administration Utilities" +.SH NAME +auditd.conf \- audit daemon configuration file +.SH DESCRIPTION +The file +.I /etc/audit/auditd.conf +contains configuration information specific to the audit daemon. Each line should contain one configuration keyword, an equal sign, and then followed by appropriate configuration information. All option names and values are case insensitive. The keywords recognized are listed and described below. Each line should be limited to 160 characters or the line will be skipped. You may add comments to the file by starting the line with a '#' character. + +.TP +.I log_file +This keyword specifies the full path name to the log file where audit records +will be stored. It must be a regular file. +.TP +.I log_format +The log format describes how the information should be stored on disk. There are 2 options: raw and nolog. +If set to +.IR RAW , +the audit records will be stored in a format exactly as the kernel sends it. If this option is set to +.I NOLOG +then all audit information is discarded instead of writing to disk. This mode does not affect data sent to the audit event dispatcher. +.TP +.I log_group +This keyword specifies the group that is applied to the log file's permissions. The default is root. The group name can be either numeric or spelled out. +.TP +.I priority_boost +This is a non-negative number that tells the audit daemon how much of a priority boost it should take. The default is 4. No change is 0. +.TP +.I flush +Valid values are +.IR none ", " incremental ", " data ", and " sync ". +If set to +.IR none , +no special effort is made to flush the audit records to disk. If set to +.IR incremental , +Then the +.I freq +parameter is used to determine how often an explicit flush to disk is issued. +The +.I data +parameter tells the audit daemon to keep the data portion of the disk file +sync'd at all times. The +.I sync +option tells the audit daemon to keep both the data and meta-data fully +sync'd with every write to disk. +.TP +.I freq +This is a non-negative number that tells the audit daemon how many records to +write before issuing an explicit flush to disk command. This value is only +valid when the +.I flush +keyword is set to +.IR incremental . +.TP +.I num_logs +This keyword specifies the number of log files to keep if rotate is given +as the +.I max_log_file_action. +If the number is < 2, logs are not rotated. This number must be 99 or less. +The default is 0 - which means no rotation. As you increase the number of log files being rotated, you may need to adjust the kernel backlog setting upwards since it takes more time to rotate the files. This is typically done in /etc/audit/audit.rules. If log rotation is configured to occur, the daemon will check for excess logs and remove them in effort to keep disk space available. The excess log check is only done on startup and when a reconfigure results in a space check. +.TP +.I disp_qos +This option controls whether you want blocking/lossless or non-blocking/lossy communication between the audit daemon and the dispatcher. There is a 128k buffer between the audit daemon and dispatcher. This is good enogh for most uses. If lossy is chosen, incoming events going to the dispatcher are discarded when this queue is full. (Events are still written to disk if log_format is not nolog.) Otherwise the auditd daemon will wait for the queue to have an empty spot before logging to disk. The risk is that while the daemon is waiting for network IO, an event is not being recorded to disk. Valid values are: lossy and lossless. Lossy is the default value. +.TP +.I dispatcher +The dispatcher is a program that is started by the audit daemon when it starts up. It will pass a copy of all audit events to that application's stdin. Make sure you trust the application that you add to this line since it runs with root privileges. +.TP +.I name_format +This option controls how computer node names are inserted into the audit event stream. It has the following choices: +.IR none ", " hostname ", " fqd ", " numeric ", and " user ". +.IR None +means that no computer name is inserted into the audit event. +.IR hostname +is the name returned by the gethostname syscall. The +.IR fqd +means that it takes the hostname and resolves it with dns for a fully qualified +domain name of that machine. +.IR Numeric +is similar to fqd except it resolves the IP address of the machine. In order to use this option, you might want to test that 'hostname \-i' or 'domainname \-i' returns a numeric address. Also, this option is not recommended if dhcp is used because you could have different addresses over time for the same machine. +.IR User +is an admin defined string from the name option. The default value is +.IR none ". +.TP +.I name +This is the admin defined string that identifies the machine if +.IR user +is given as the +.IR name_format +option. +.TP +.I max_log_file +This keyword specifies the maximum file size in megabytes. When this limit +is reached, it will trigger a configurable action. The value given must be numeric. +.TP +.I max_log_file_action +This parameter tells the system what action to take when the system has +detected that the max file size limit has been reached. Valid values are +.IR ignore ", " syslog ", " suspend ", " rotate " and "keep_logs. +If set to +.IR ignore , +the audit daemon does nothing. +.IR syslog +means that it will issue a warning to syslog. +.IR suspend +will cause the audit daemon to stop writing records to the disk. The daemon will still be alive. The +.IR rotate +option will cause the audit daemon to rotate the logs. It should be noted that logs with higher numbers are older than logs with lower numbers. This is the same convention used by the logrotate utility. The +.IR keep_logs +option is similar to rotate except it does not use the num_logs setting. This prevents audit logs from being overwritten. The effect is that logs accumulate and are not deleted \- which will trigger the +.I space_left_action +if the volume fills up. This is best used in combination with an external script used to archive logs on a periodic basis. +.TP +.I action_mail_acct +This option should contain a valid email address or alias. The default address is root. If the email address is not local to the machine, you must make sure you have email properly configured on your machine and network. Also, this option requires that /usr/lib/sendmail exists on the machine. +.TP +.I space_left +This is a numeric value in megabytes that tells the audit daemon when +to perform a configurable action because the system is starting to run low on disk space. +.TP +.I space_left_action +This parameter tells the system what action to take when the system has +detected that it is starting to get low on disk space. +Valid values are +.IR ignore ", " syslog ", " rotate ", " email ", " exec ", " suspend ", " single ", and " halt . +If set to +.IR ignore , +the audit daemon does nothing. +.I syslog +means that it will issue a warning to syslog. +.I rotate +will rotate logs, losing the oldest to free up space. +.I Email +means that it will send a warning to the email account specified in +.I action_mail_acct +as well as sending the message to syslog. +.I exec +/path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action. This can be done by adding service auditd resume to the script. +.I suspend +will cause the audit daemon to stop writing records to the disk. The daemon will still be alive. The +.I single +option will cause the audit daemon to put the computer system in single user mode. The +.I halt +option will cause the audit daemon to shutdown the computer system. +.TP +.I admin_space_left +This is a numeric value in megabytes that tells the audit daemon when +to perform a configurable action because the system +.B is running low +on disk space. This should be considered the last chance to do something before running out of disk space. The numeric value for this parameter should be lower than the number for space_left. +.TP +.I admin_space_left_action +This parameter tells the system what action to take when the system has +detected that it +.B is low on disk space. +Valid values are +.IR ignore ", " syslog ", "rotate ", " email ", " exec ", " suspend ", " single ", and " halt . +If set to +.IR ignore , +the audit daemon does nothing. +.I Syslog +means that it will issue a warning to syslog. +.I rotate +will rotate logs, losing the oldest to free up space. +.I Email +means that it will send a warning to the email account specified in +.I action_mail_acct +as well as sending the message to syslog. +.I exec +/path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action. This can be done by adding service auditd resume to the script. +.I Suspend +will cause the audit daemon to stop writing records to the disk. The daemon will still be alive. The +.I single +option will cause the audit daemon to put the computer system in single user mode. The +.I halt +option will cause the audit daemon to shutdown the computer system. +.TP +.I disk_full_action +This parameter tells the system what action to take when the system has +detected that the partition to which log files are written has become full. Valid values are +.IR ignore ", " syslog ", " rotate ", " exec ", " suspend ", " single ", and " halt . +If set to +.IR ignore , +the audit daemon will issue a syslog message but no other action is taken. +.I Syslog +means that it will issue a warning to syslog. +.I rotate +will rotate logs, losing the oldest to free up space. +.I exec +/path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume loggin +g once its completed its action. This can be done by adding service auditd resume to the script. +.I Suspend +will cause the audit daemon to stop writing records to the disk. The daemon will still be alive. The +.I single +option will cause the audit daemon to put the computer system in single user mode. +.I halt +option will cause the audit daemon to shutdown the computer system. +.TP +.I disk_error_action +This parameter tells the system what action to take whenever there is an error +detected when writing audit events to disk or rotating logs. Valid values are +.IR ignore ", " syslog ", " exec ", " suspend ", " single ", and " halt . +If set to +.IR ignore , +the audit daemon will not take any action. +.I Syslog +means that it will issue no more than 5 consecutive warnings to syslog. +.I exec +/path-to-script will execute the script. You cannot pass parameters to the script. +.I Suspend +will cause the audit daemon to stop writing records to the disk. The daemon will still be alive. The +.I single +option will cause the audit daemon to put the computer system in single user mode. +.I halt +option will cause the audit daemon to shutdown the computer system. +.TP +.I tcp_listen_port +This is a numeric value in the range 1..65535 which, if specified, +causes auditd to listen on the corresponding TCP port for audit +records from remote systems. The audit daemon may be linked with +tcp_wrappers. You may want to control access with an entry in the +hosts.allow and deny files. +.TP +.I tcp_listen_queue +This is a numeric value which indicates how many pending (requested +but unaccepted) connections are allowed. The default is 5. Setting +this too small may cause connections to be rejected if too many hosts +start up at exactly the same time, such as after a power failure. +.TP +.I tcp_max_per_addr +This is a numeric value which indicates how many concurrent connections from +one IP address is allowed. The default is 1 and the maximum is 1024. Setting +this too large may allow for a Denial of Service attack on the logging +server. Also note that the kernel has an internal maximum that will eventually +prevent this even if auditd allows it by config. The default should be adequate +in most cases unless a custom written recovery script runs to forward unsent +events. In this case you would increase the number only large enough to let it +in too. +.TP +.I use_libwrap +This setting determines whether or not to use tcp_wrappers to discern connection attempts that are from allowed machines. Legal values are either +.IR yes ", or " no " +The default value is yes. +.TP +.I tcp_client_ports +This parameter may be a single numeric value or two values separated +by a dash (no spaces allowed). It indicates which client ports are +allowed for incoming connections. If not specified, any port is +allowed. Allowed values are 1..65535. For example, to require the +client use a priviledged port, specify +.I 1\-1023 +for this parameter. You will also need to set the local_port option in the audisp-remote.conf file. Making sure that clients send from a privileged port is a security feature to prevent log injection attacks by untrusted users. +.TP +.I tcp_client_max_idle +This parameter indicates the number of seconds that a client may be idle (i.e. no data from them at all) before auditd complains. This is used to close inactive connections if the client machine has a problem where it cannot shutdown the connection cleanly. Note that this is a global setting, and must be higher than any individual client heartbeat_timeout setting, preferably by a factor of two. The default is zero, which disables this check. +.TP +.I enable_krb5 +If set to "yes", Kerberos 5 will be used for authentication and +encryption. The default is "no". +.TP +.I krb5_principal +This is the principal for this server. The default is "auditd". +Given this default, the server will look for a key named like +.I auditd/hostname@EXAMPLE.COM +stored in +.I /etc/audit/audit.key +to authenticate itself, where hostname is the canonical name for the +server's host, as returned by a DNS lookup of its IP address. +.TP +.I krb5_key_file +Location of the key for this client's principal. +Note that the key file must be owned by root and mode 0400. +The default is +.I /etc/audit/audit.key + +.SH NOTES +In a CAPP environment, the audit trail is considered so important that access to system resources must be denied if an audit trail cannot be created. In this environment, it would be suggested that /var/log/audit be on its own partition. This is to ensure that space detection is accurate and that no other process comes along and consumes part of it. +.PP +The flush parameter should be set to sync or data. +.PP +Max_log_file and num_logs need to be adjusted so that you get complete use of your partition. It should be noted that the more files that have to be rotated, the longer it takes to get back to receiving audit events. Max_log_file_action should be set to keep_logs. +.PP +Space_left should be set to a number that gives the admin enough time to react to any alert message and perform some maintenance to free up disk space. This would typically involve running the \fBaureport \-t\fP report and moving the oldest logs to an archive area. The value of space_left is site dependent since the rate at which events are generated varies with each deployment. The space_left_action is recommended to be set to email. If you need something like an snmp trap, you can use the exec option to send one. +.PP +Admin_space_left should be set to the amount of disk space on the audit partition needed for admin actions to be recorded. Admin_space_left_action would be set to single so that use of the machine is restricted to just the console. +.PP +The disk_full_action is triggered when no more room exists on the partition. All access should be terminated since no more audit capability exists. This can be set to either single or halt. +.PP +The disk_error_action should be set to syslog, single, or halt depending on your local policies regarding handling of hardware malfunctions. +.PP +Specifying a single allowed client port may make it difficult for the +client to restart their audit subsystem, as it will be unable to +recreate a connection with the same host addresses and ports until the +connection closure TIME_WAIT state times out. + +.SH FILES +.TP +.I /etc/audit/auditd.conf +Audit daemon configuration file + +.SH "SEE ALSO" +.BR auditd (8), +.BR audisp\-remote.conf (5). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/augenrules.8 b/framework/src/audit/docs/augenrules.8 new file mode 100644 index 00000000..e667bc20 --- /dev/null +++ b/framework/src/audit/docs/augenrules.8 @@ -0,0 +1,41 @@ +.TH AUGENRULES: "8" "Apr 2013" "Red Hat" "System Administration Utilities" +.SH NAME +augenrules \- a script that merges component audit rule files +.SH SYNOPSIS +.B augenrules +.RI [ \-\-check ]\ [ \-\-load ] +.SH DESCRIPTION +\fBaugenrules\fP is a script that merges all component audit rules files, +found in the audit rules directory, \fI/etc/audit/rules.d\fP, placing the +merged file in \fI/etc/audit/audit.rules\fP. Component audit rule files, must +end in \fI.rules\fP in order to be processed. All other files in +\fI/etc/audit/rules.d\fP are ignored. +.P +The files are concatenated in order, based on their natural sort (see -v option of ls(1)) and stripped of empty and comment (#) lines. +.P +The last processed -\fID\fP directive without an option, if present, is always +emitted as the first line in the resultant file. Those with an option are +replicated in place. +The last processed -\fIb\fP directive, if present, is always +emitted as the second line in the resultant file. +The last processed -\fIf\fP directive, if present, is always +emitted as the third line in the resultant file. +The last processed -\fIe\fP directive, if present, is always +emitted as the last line in the resultant file. +.P +The generated file is only copied to \fI/etc/audit/audit.rules\fP, if it differs. +.SH OPTIONS +.TP +.B \-\-check +test if rules have changed and need updating without overwriting audit.rules. +.TP +.B \-\-load +load old or newly built rules into the kernel. + +.SH FILES +/etc/audit/rules.d/ +/etc/audit/audit.rules +.SH "SEE ALSO" +.BR audit.rules (8), +.BR auditctl (8), +.BR auditd (8). diff --git a/framework/src/audit/docs/auparse_add_callback.3 b/framework/src/audit/docs/auparse_add_callback.3 new file mode 100644 index 00000000..82a03f28 --- /dev/null +++ b/framework/src/audit/docs/auparse_add_callback.3 @@ -0,0 +1,69 @@ +.TH "AUPARSE_ADD_CALLBACK" "3" "May 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_add_callback \- add a callback handler for notifications +.SH "SYNOPSIS" +.B #include +.sp +.nf +.B void +auparse_add_callback(auparse_state_t *au, auparse_callback_ptr callback, + void *user_data, user_destroy user_destroy_func); +.fi +.SH "DESCRIPTION" +auparse_add_callback adds a callback function to the parse state which is invoked to notify the application of parsing events. This is part of the event feed API. + +The signature of the callback is: + +.nf +void +auparse_callback(auparse_state_t *au, auparse_cb_event_t cb_event_type, + void *user_data); +.fi + +When the callback is invoked it is passed: + +.TP +.I au + a pointer to the parse_state +.TP +.I cb_event_type +enumerated value indicating the reason why the callback was invoked +.TP +.I user_data +pointer to user supplied private data. May be NULL. +. +.TP +.I user_destroy_func +pointer to function called when user_data is destroyed. May be NULL. +The signature is: +.br +.sp +.nf +void destroy(void *user_data); +.fi +.br +.sp +The destroy() function should be prepared to accept user_data possibly being NULL. +.PP +The +.I cb_event_type +argument indicates why the callback was invoked. It's possible values are: +.br +.TP +.B AUPARSE_CB_EVENT_READY +A complete event has been parsed and is ready to be examined. This is logically equivalent to the parse state immediately following +.I auparse_next_event() +.PP +See auparse_feed(3) for a complete code example. +. +.SH "RETURN VALUE" + +Returns the previous callback pointer. + +.SH "SEE ALSO" + +.BR auparse_feed (3), +.BR auparse_flush_feed (3). + +.SH AUTHOR +John Dennis diff --git a/framework/src/audit/docs/auparse_destroy.3 b/framework/src/audit/docs/auparse_destroy.3 new file mode 100644 index 00000000..e5a82c75 --- /dev/null +++ b/framework/src/audit/docs/auparse_destroy.3 @@ -0,0 +1,23 @@ +.TH "AUPARSE_DESTROY" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_destroy \- release instance of parser +.SH "SYNOPSIS" +.B #include +.sp +void auparse_destroy (auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_destroy frees all data structures and closes file descriptors. + +.SH "RETURN VALUE" + +None. + +.SH "SEE ALSO" + +.BR auparse_init (3), +.BR auparse_reset (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_feed.3 b/framework/src/audit/docs/auparse_feed.3 new file mode 100644 index 00000000..f3310e1b --- /dev/null +++ b/framework/src/audit/docs/auparse_feed.3 @@ -0,0 +1,111 @@ +.TH "AUPARSE_FEED" "3" "May 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_feed \- feed data into parser +.SH "SYNOPSIS" +.B #include +.sp +.nf +int auparse_feed(auparse_state_t *au, const char *data, size_t data_len); +.fi + +.TP +.I au +The audit parse state +.TP +.I data +a buffer of data to feed into the parser, it is +.I data_len +bytes long. The data is copied in the parser, upon return the caller may free or reuse the data buffer. +.TP +.I data_len +number of bytes in +.I data + +.SH "DESCRIPTION" + +.I auparse_feed +supplies new data for the parser to consume. +.I auparse_init() +must have been called with a source type of AUSOURCE_FEED and a NULL pointer. +.br +.sp +The parser consumes as much data +as it can invoking a user supplied callback specified with +.I auparse_add_callback +with a cb_event_type of +.I AUPARSE_CB_EVENT_READY +each time the parser recognizes a complete event in the data stream. Data not fully parsed will persist and be +prepended to the next feed data. After all data has been feed to the parser +.I auparse_flush_feed +should be called to signal the end of input data and flush any pending parse data through the parsing system. + +.SH "EXAMPLE" +.nf +void +auparse_callback(auparse_state_t *au, auparse_cb_event_t cb_event_type, + void *user_data) +{ + int *event_cnt = (int *)user_data; + + if (cb_event_type == AUPARSE_CB_EVENT_READY) { + if (auparse_first_record(au) <= 0) return; + printf("event: %d\\n", *event_cnt); + printf("records:%d\\n", auparse_get_num_records(au)); + do { + printf("fields:%d\\n", auparse_get_num_fields(au)); + printf("type=%d ", auparse_get_type(au)); + const au_event_t *e = auparse_get_timestamp(au); + if (e == NULL) return; + printf("event time: %u.%u:%lu\\n", + (unsigned)e\->sec, e\->milli, e\->serial); + auparse_first_field(au); + do { + printf("%s=%s (%s)\\n", auparse_get_field_name(au), + auparse_get_field_str(au), + auparse_interpret_field(au)); + } while (auparse_next_field(au) > 0); + printf("\\n"); + + } while(auparse_next_record(au) > 0); + (*event_cnt)++; + } +} + +main(int argc, char **argv) +{ + char *filename = argv[1]; + FILE *fp; + char buf[256]; + size_t len; + int *event_cnt = malloc(sizeof(int)); + + au = auparse_init(AUSOURCE_FEED, 0); + + *event_cnt = 1; + auparse_add_callback(au, auparse_callback, event_cnt, free); + + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "could not open '%s', %s\\n", filename, strerror(errno)); + return 1; + } + + while ((len = fread(buf, 1, sizeof(buf), fp))) { + auparse_feed(au, buf, len); + } + auparse_flush_feed(au); +} +.fi + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR auparse_add_callback (3), +.BR auparse_flush_feed (3), +.BR auparse_feed_has_data (3) + + +.SH AUTHOR +John Dennis diff --git a/framework/src/audit/docs/auparse_feed_has_data.3 b/framework/src/audit/docs/auparse_feed_has_data.3 new file mode 100644 index 00000000..d048ab21 --- /dev/null +++ b/framework/src/audit/docs/auparse_feed_has_data.3 @@ -0,0 +1,29 @@ +.TH "AUPARSE_FEED_HAS_DATA" "3" "Sept 2012" "Red Hat" "Linux Audit API" +.SH NAME +auparse_feed_has_data \- check if there is any data accumulating that might need flushing. +.SH "SYNOPSIS" +.B #include +.sp +.nf +int auparse_feed_has_data(const auparse_state_t *au); +.fi + +.TP +.I au +The audit parse state +.SH "DESCRIPTION" + +.I auparse_feed_has_data +may be called to determine if there is any records that are accumulating but not yet ready to emit. + +.SH "RETURN VALUE" + +Returns 1 if any records are accumulating otherwise 0 if empty. + +.SH "SEE ALSO" + +.BR auparse_feed (3) + + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_find_field.3 b/framework/src/audit/docs/auparse_find_field.3 new file mode 100644 index 00000000..4062588f --- /dev/null +++ b/framework/src/audit/docs/auparse_find_field.3 @@ -0,0 +1,23 @@ +.TH "AUPARSE_FIND_FIELD" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_find_field \- search for field name +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_find_field(auparse_state_t *au, const char *name); + +.SH "DESCRIPTION" + +auparse_find_field will scan all records in an event to find the first occurance of the field name passed to it. Searching begins from the cursor's current position. The field name is stored for subsequent searching. + +.SH "RETURN VALUE" + +Returns NULL field not found. If an error occurs errno will be set. Otherwise, it returns a pointer to the text value associated with the field. + +.SH "SEE ALSO" + +.BR auparse_first_record (3), +.BR auparse_find_field_next (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_find_field_next.3 b/framework/src/audit/docs/auparse_find_field_next.3 new file mode 100644 index 00000000..f072fe71 --- /dev/null +++ b/framework/src/audit/docs/auparse_find_field_next.3 @@ -0,0 +1,24 @@ +.TH "AUPARSE_FIND_FIELD_NEXT" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_find_field_next \- find next occurrance of field name +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_find_field_next(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_find_field_next finds the next occurrance of the previously stored field name. It will scan until it reaches the last record of the current event. + +.SH "RETURN VALUE" + +Returns NULL field not found. If an error occurs errno will be set. Otherwise, it returns a pointer to the text value associated with the field. + +.SH "SEE ALSO" + +.BR auparse_first_record (3), +.BR auparse_next_event (3), +.BR auparse_find_field (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_first_field.3 b/framework/src/audit/docs/auparse_first_field.3 new file mode 100644 index 00000000..b57277eb --- /dev/null +++ b/framework/src/audit/docs/auparse_first_field.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_FIRST_FIELD" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_first_field \- reposition field cursor +.SH "SYNOPSIS" +.B #include +.sp +int auparse_first_field(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_first_field repositions the library's internal cursor to point to the first field of the current record in the current event. + +.SH "RETURN VALUE" + +Returns 0 if there is no event data; otherwise, 1 for success. + +.SH "SEE ALSO" + +.BR auparse_next_field (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_first_record.3 b/framework/src/audit/docs/auparse_first_record.3 new file mode 100644 index 00000000..2cdbc9c9 --- /dev/null +++ b/framework/src/audit/docs/auparse_first_record.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_FIRST_RECORD" "3" "Sep 2014" "Red Hat" "Linux Audit API" +.SH NAME +auparse_first_record \- reposition record cursor +.SH "SYNOPSIS" +.B #include +.sp +int auparse_first_record(auparse_state_t *au); + +.SH "DESCRIPTION" +auparse_first_record repositions the internal cursors of the parsing library to point to the first field of the first record in the current event. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs, 0 if there is no event data, or 1 for success. + +.SH "SEE ALSO" + +.BR auparse_next_event (3), +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_flush_feed.3 b/framework/src/audit/docs/auparse_flush_feed.3 new file mode 100644 index 00000000..905f2e9e --- /dev/null +++ b/framework/src/audit/docs/auparse_flush_feed.3 @@ -0,0 +1,30 @@ +.TH "AUPARSE_FLUSH_FEED" "3" "Sept 2012" "Red Hat" "Linux Audit API" +.SH NAME +auparse_flush_feed \- flush any unconsumed feed data through parser. +.SH "SYNOPSIS" +.B #include +.sp +.nf +int auparse_feed(auparse_state_t *au); +.fi + +.TP +.I au +The audit parse state +.SH "DESCRIPTION" + +.I auparse_flush_feed +should be called to signal the end of feed input data and flush any pending parse data through the parsing system. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR auparse_feed (3), +.BR auparse_feed_has_data (3) + + +.SH AUTHOR +John Dennis diff --git a/framework/src/audit/docs/auparse_get_field_int.3 b/framework/src/audit/docs/auparse_get_field_int.3 new file mode 100644 index 00000000..a7464c2c --- /dev/null +++ b/framework/src/audit/docs/auparse_get_field_int.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_GET_FIELD_INT" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_field_int \- get current field's value as an int +.SH "SYNOPSIS" +.B #include +.sp +int auparse_get_field_int(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_field_int allows access to the value as an int of the current field of the current record in the current event. + +.SH "RETURN VALUE" + +Returns \-1 if there is an error with errno set appropriately or the value if errno is zero. + +.SH "SEE ALSO" + +.BR auparse_get_field_str (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_field_name.3 b/framework/src/audit/docs/auparse_get_field_name.3 new file mode 100644 index 00000000..e1f68b3f --- /dev/null +++ b/framework/src/audit/docs/auparse_get_field_name.3 @@ -0,0 +1,24 @@ +.TH "AUPARSE_GET_FIELD_NAME" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_field_name \- get current field's name +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_get_field_name(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_field_name allows access to the current field name of the current record in the current event. + +.SH "RETURN VALUE" + +Returns NULL if an error occurs; otherwise, a pointer to the field's name. + +.SH "SEE ALSO" + +.BR auparse_get_field_str (3), +.BR auparse_interpret_field (3), +.BR auparse_next_field (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_field_str.3 b/framework/src/audit/docs/auparse_get_field_str.3 new file mode 100644 index 00000000..e1ebfced --- /dev/null +++ b/framework/src/audit/docs/auparse_get_field_str.3 @@ -0,0 +1,24 @@ +.TH "AUPARSE_GET_FIELD_STR" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_field_str \- get current field's value +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_get_field_str(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_field_str allows access to the value in the current field of the current record in the current event. + +.SH "RETURN VALUE" + +Returns NULL if an error occurs; otherwise, a pointer to the field's value. + +.SH "SEE ALSO" + +.BR auparse_get_field_name (3), +.BR auparse_interpret_field (3), +.BR auparse_next_field (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_field_type.3 b/framework/src/audit/docs/auparse_get_field_type.3 new file mode 100644 index 00000000..53fec8d0 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_field_type.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_GET_FIELD_TYPE" "3" "Sept 2008" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_field_type \- get current field's data type +.SH "SYNOPSIS" +.B #include +.sp +int auparse_get_field_type(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_field_type returns a value from the auparse_type_t enum that describes the kind of data in the current field of the current record in the current event. + +.SH "RETURN VALUE" + +Returns AUPARSE_TYPE_UNCLASSIFIED if the field's data type has no known description or is an integer. Otherwise it returns another enum. Fields with the type AUPARSE_TYPE_ESCAPED must be interpretted to access their value since those field's raw value is encoded. + +.SH "SEE ALSO" + +.BR auparse_get_field_name (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_filename.3 b/framework/src/audit/docs/auparse_get_filename.3 new file mode 100644 index 00000000..259a8e25 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_filename.3 @@ -0,0 +1,26 @@ +.TH "AUPARSE_GET_FILENAME" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_filename \- get the filename where record was found +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_get_filename(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_filename will return the name of the source file where the +record was found if the source type is AUSOURCE_FILE or +AUSOURCE_FILE_ARRAY. For other source types the return value will be +NULL. + +.SH "RETURN VALUE" + +Returns pointer to a filename or NULL if unavailable. + +.SH "SEE ALSO" + +.BR auparse_get_line_number (3). +.BR auparse_next_record (3). + +.SH AUTHOR +John Dennis diff --git a/framework/src/audit/docs/auparse_get_line_number.3 b/framework/src/audit/docs/auparse_get_line_number.3 new file mode 100644 index 00000000..bd0c4177 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_line_number.3 @@ -0,0 +1,27 @@ +.TH "AUPARSE_GET_LINE_NUMBER" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_line_number \- get line number where record was found +.SH "SYNOPSIS" +.B #include +.sp +unsigned int auparse_get_line_number(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_line_number will return the source input line number for +the current record of the current event. Line numbers start at 1. If +the source input type is AUSOURCE_FILE_ARRAY the line numbering will +reset back to 1 each time a new life in the file array is opened. + +.SH "RETURN VALUE" + +Returns the line number. Line numbers are 1 based, a zero value +indicates the line number information is unavailable. + +.SH "SEE ALSO" + +.BR auparse_get_filename (3). +.BR auparse_next_record (3). + +.SH AUTHOR +John Dennis diff --git a/framework/src/audit/docs/auparse_get_milli.3 b/framework/src/audit/docs/auparse_get_milli.3 new file mode 100644 index 00000000..3000988e --- /dev/null +++ b/framework/src/audit/docs/auparse_get_milli.3 @@ -0,0 +1,25 @@ +.TH "AUPARSE_GET_MILLI" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_milli \- get the millisecond value of the event +.SH "SYNOPSIS" +.B #include +.sp +unsigned int auparse_get_milli(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_milli gets the millisecond value of the current event. + +.SH "RETURN VALUE" + +Returns 0 if an error occurs; otherwise, the value of the millisecond portion of the timestamp. + +.SH "SEE ALSO" + +.BR auparse_get_timestamp (3), +.BR auparse_get_time (3). +.BR auparse_get_milli (3). +.BR auparse_get_node (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_node.3 b/framework/src/audit/docs/auparse_get_node.3 new file mode 100644 index 00000000..41731406 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_node.3 @@ -0,0 +1,25 @@ +.TH "AUPARSE_GET_NODE" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_node \- get the event's machine node name +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_get_node(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_node gets the machine's node name if it exists in the audit event from the current event's timestamp data structure. Not all records have node names since its an admin configurable option. + +.SH "RETURN VALUE" + +Returns a copy of the node name or NULL if it does not exist or there was an error. The caller must free the string. + +.SH "SEE ALSO" + +.BR auparse_get_timestamp (3), +.BR auparse_get_time (3), +.BR auparse_get_milli (3). +.BR auparse_get_serial (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_num_fields.3 b/framework/src/audit/docs/auparse_get_num_fields.3 new file mode 100644 index 00000000..595fa56b --- /dev/null +++ b/framework/src/audit/docs/auparse_get_num_fields.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_GET_NUM_FIELDS" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_num_fields \- get the number of fields +.SH "SYNOPSIS" +.B #include +.sp +unsigned int auparse_get_num_fields(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_num_fields gets the number of fields in the current record of the current event. + +.SH "RETURN VALUE" + +Returns 0 if an error occurs; otherwise, the number of fields. + +.SH "SEE ALSO" + +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_num_records.3 b/framework/src/audit/docs/auparse_get_num_records.3 new file mode 100644 index 00000000..b1d3f3a2 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_num_records.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_GET_NUM_RECORDS" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_num_records \- get the number of records +.SH "SYNOPSIS" +.B #include +.sp +unsigned int auparse_get_num_records(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_num_records gets the number of records in the current event. + +.SH "RETURN VALUE" + +Returns 0 if an error occurs; otherwise, the number of records. + +.SH "SEE ALSO" + +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_record_text.3 b/framework/src/audit/docs/auparse_get_record_text.3 new file mode 100644 index 00000000..06f5bde9 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_record_text.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_GET_RECORD_TEXT" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_record_text \- access unparsed record data +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_get_record_text(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_record_text returns a pointer to the full unparsed record. + +.SH "RETURN VALUE" + +Returns NULL if an error occurs; otherwise, a pointer to the record. + +.SH "SEE ALSO" + +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_serial.3 b/framework/src/audit/docs/auparse_get_serial.3 new file mode 100644 index 00000000..e22b80e5 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_serial.3 @@ -0,0 +1,25 @@ +.TH "AUPARSE_GET_SERIAL" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_serial \- get the event's serial number +.SH "SYNOPSIS" +.B #include +.sp +unsigned long auparse_get_serial(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_serial gets the serial number value from the current event's timestamp data structure. + +.SH "RETURN VALUE" + +Returns 0 if an error occurs; otherwise, the serial number for the event. + +.SH "SEE ALSO" + +.BR auparse_get_timestamp (3), +.BR auparse_get_time (3), +.BR auparse_get_milli (3). +.BR auparse_get_node (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_time.3 b/framework/src/audit/docs/auparse_get_time.3 new file mode 100644 index 00000000..227ef127 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_time.3 @@ -0,0 +1,26 @@ +.TH "AUPARSE_GET_TIME" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_time \- get event's time +.SH "SYNOPSIS" +.B #include +.sp +time_t auparse_get_time(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_time will access just the time portion of the timestamp data structure for the current event. + +.SH "RETURN VALUE" + +Returns 0 if an error occurs; otherwise, the valid time value in time_t format. + +.SH "SEE ALSO" + +.BR time (3), +.BR auparse_get_timestamp (3), +.BR auparse_get_milli (3). +.BR auparse_get_serial (3). +.BR auparse_get_node (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_timestamp.3 b/framework/src/audit/docs/auparse_get_timestamp.3 new file mode 100644 index 00000000..71a66136 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_timestamp.3 @@ -0,0 +1,36 @@ +.TH "AUPARSE_GET_TIMESTAMP" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_timestamp \- access timestamp of the event +.SH "SYNOPSIS" +.B #include +.sp +const au_event_t *auparse_get_timestamp(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_timestamp provides an accessor function for the event's timestamp data structure. The data structure is as follows: + +.nf +typedef struct +{ + time_t sec; // Event seconds + unsigned int milli; // millisecond of the timestamp + unsigned long serial; // Serial number of the event + const char *host; // Machine's node name +} au_event_t; +.fi + +.SH "RETURN VALUE" + +Returns NULL if an error occurs; otherwise, a valid pointer to the data. + +.SH "SEE ALSO" + +.BR auparse_get_time (3), +.BR auparse_get_milli (3), +.BR auparse_get_serial (3), +.BR auparse_get_node (3), +.BR auparse_timestamp_compare (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_get_type.3 b/framework/src/audit/docs/auparse_get_type.3 new file mode 100644 index 00000000..c278e914 --- /dev/null +++ b/framework/src/audit/docs/auparse_get_type.3 @@ -0,0 +1,23 @@ +.TH "AUPARSE_GET_TYPE" "3" "Jan 2014" "Red Hat" "Linux Audit API" +.SH NAME +auparse_get_type \- get record's type +.SH "SYNOPSIS" +.B #include +.sp +int auparse_get_type(auparse_state_t *au); +const char *auparse_get_type_name(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_get_type will return the integer value for the current record of the current event. The auparse_get_type_name function will return the text representation of the name of the current record type. + +.SH "RETURN VALUE" + +auparse_get_type returns 0 if an error occurs; otherwise, the record's type. The auparse_get_type_name function returns NULL on error; otherwise a pointer to a string. + +.SH "SEE ALSO" + +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_goto_record_num.3 b/framework/src/audit/docs/auparse_goto_record_num.3 new file mode 100644 index 00000000..0688d969 --- /dev/null +++ b/framework/src/audit/docs/auparse_goto_record_num.3 @@ -0,0 +1,21 @@ +.TH "AUPARSE_GOTO_RECORD_NUM" "3" "May 2008" "Red Hat" "Linux Audit API" +.SH NAME +auparse_goto_record_num \- move record cursor to specific record +.SH "SYNOPSIS" +.B #include +.sp +int auparse_goto_record_num(auparse_state_t *au, unsigned int num); + +.SH "DESCRIPTION" +auparse_goto_record_num will move the internal library cursors to point to a specific physical record number. Records within the same event are numbered starting from 0. This is generally not needed but there are some cases where one may want precise control over the exact record being looked at. + +.SH "RETURN VALUE" + +Returns 0 on error or 1 for success. + +.SH "SEE ALSO" + +.BR auparse_get_num_records (3), auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_init.3 b/framework/src/audit/docs/auparse_init.3 new file mode 100644 index 00000000..7dd2b521 --- /dev/null +++ b/framework/src/audit/docs/auparse_init.3 @@ -0,0 +1,37 @@ +.TH "AUPARSE_INIT" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_init \- initialize an instance of the audit parsing library +.SH "SYNOPSIS" +.B #include +.sp +auparse_state_t *auparse_init(ausource_t source, const void *b); + +.SH "DESCRIPTION" + +auparse_init initializes an instance of the audit parsing library. The function returns an opaque pointer to the parser's internal state. It is used in subsequent calls to the library so. The source variable determines where the library looks for data. Legal values can be: + +.nf + AUSOURCE_LOGS - use audit logs + AUSOURCE_FILE - use a file + AUSOURCE_FILE_ARRAY - use several files + AUSOURCE_BUFFER - use a buffer + AUSOURCE_BUFFER_ARRAY - use an array of buffers + AUSOURCE_DESCRIPTOR - use a particular descriptor + AUSOURCE_FILE_POINTER - use a stdio FILE pointer + AUSOURCE_FEED - feed data to parser with auparse_feed() +.fi + +The pointer 'b' is used to set the file name, array of filenames, the buffer address, or an array of pointers to buffers, or the descriptor number based on what source is given. When the data source is an array of files or buffers, you would create an array of pointers with the last one being a NULL pointer. Buffers should be NUL terminated. + +.SH "RETURN VALUE" + +Returns a NULL pointer if an error occurs; otherwise, the return value is an opaque pointer to the parser's internal state. + +.SH "SEE ALSO" + +.BR auparse_reset (3), +.BR auparse_destroy (3). +.BR auparse_feed (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_interpret_field.3 b/framework/src/audit/docs/auparse_interpret_field.3 new file mode 100644 index 00000000..2ff5297b --- /dev/null +++ b/framework/src/audit/docs/auparse_interpret_field.3 @@ -0,0 +1,24 @@ +.TH "AUPARSE_INTERPRET_FIELD" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_interpret_field \- get current field's value interpreted +.SH "SYNOPSIS" +.B #include +.sp +const char *auparse_interpret_field(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_interpret_field allows access to the interpreted value in the current field of the current record in the current event. The returned value will be destroyed if you call this function again. If you need to interpret another field and keep this value, you will have to copy it for later use. + +Examples of things that could be interpreted are: uid, gid, syscall numbers, exit codes, file paths, socket addresses, permissions, modes, and capabilities. There are likely to be more in the future. If a value cannot be interpreted, its original value is returned. + +.SH "RETURN VALUE" + +Returns NULL if there is an error otherwise a pointer to the interpreted value. + +.SH "SEE ALSO" + +.BR auparse_get_field_str (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_next_event.3 b/framework/src/audit/docs/auparse_next_event.3 new file mode 100644 index 00000000..b5a66e94 --- /dev/null +++ b/framework/src/audit/docs/auparse_next_event.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_NEXT_EVENT" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_next_event \- get the next event +.SH "SYNOPSIS" +.B #include +.sp +int auparse_next_event(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_next_event will position the cursors at the first field of the first record of the next event in a file or buffer. It does not skip events or honor any search criteria that may be stored. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs, 0 if there's no data, 1 for success. + +.SH "SEE ALSO" + +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_next_field.3 b/framework/src/audit/docs/auparse_next_field.3 new file mode 100644 index 00000000..17b0c216 --- /dev/null +++ b/framework/src/audit/docs/auparse_next_field.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_NEXT_FIELD" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_next_field \- move field cursor +.SH "SYNOPSIS" +.B #include +.sp +int auparse_next_field(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_next_field moves the library's internal cursor to point to the next field in the current record of the current event. + +.SH "RETURN VALUE" + +Returns 0 if no more fields exist and 1 for success. + +.SH "SEE ALSO" + +.BR auparse_next_record (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_next_record.3 b/framework/src/audit/docs/auparse_next_record.3 new file mode 100644 index 00000000..a26a9573 --- /dev/null +++ b/framework/src/audit/docs/auparse_next_record.3 @@ -0,0 +1,21 @@ +.TH "AUPARSE_NEXT_RECORD" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_next_record \- move record cursor +.SH "SYNOPSIS" +.B #include +.sp +int auparse_next_record(auparse_state_t *au); + +.SH "DESCRIPTION" +auparse_next_record will move the internal library cursors to point to the next record of the current event. You should not call this function from a feed interface callback function. Doing so will deadlock the code. In that scenario, you should check the number of records in the current event with auparse_get_num_records and only call this if there are more records. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs, 0 if no more records in current event, or 1 for success. + +.SH "SEE ALSO" + +.BR auparse_next_event (3), auparse_get_num_records (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_node_compare.3 b/framework/src/audit/docs/auparse_node_compare.3 new file mode 100644 index 00000000..869f9454 --- /dev/null +++ b/framework/src/audit/docs/auparse_node_compare.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_NODE_COMPARE" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_node_compare \- compares node name values +.SH "SYNOPSIS" +.B #include +.sp +int auparse_node_compare(au_event_t *e1, au_event_t *e2); + +.SH "DESCRIPTION" + +auparse_node_compare compares the node name values of 2 events. + +.SH "RETURN VALUE" + +Returns \-1, 0, or 1 respectively depending on whether e2 is less than, equal to, or greater than e1. Since this is a string compare, it probably only matter that they are equal or not equal. + +.SH "SEE ALSO" + +.BR auparse_get_timestamp (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_reset.3 b/framework/src/audit/docs/auparse_reset.3 new file mode 100644 index 00000000..943fb962 --- /dev/null +++ b/framework/src/audit/docs/auparse_reset.3 @@ -0,0 +1,23 @@ +.TH "AUPARSE_RESET" "3" "Sep 2014" "Red Hat" "Linux Audit API" +.SH NAME +auparse_reset \- reset audit parser instance +.SH "SYNOPSIS" +.B #include +.sp +int auparse_reset(auparse_state_t *au); + +.SH "DESCRIPTION" + +auparse_reset resets all internal cursors to the beginning. It closes files, descriptors, and frees memory buffers. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR auparse_init (3), +.BR auparse_destroy (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/auparse_timestamp_compare.3 b/framework/src/audit/docs/auparse_timestamp_compare.3 new file mode 100644 index 00000000..8f71749d --- /dev/null +++ b/framework/src/audit/docs/auparse_timestamp_compare.3 @@ -0,0 +1,22 @@ +.TH "AUPARSE_TIMESTAMP_COMPARE" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +auparse_timestamp_compare \- compares timestamp values +.SH "SYNOPSIS" +.B #include +.sp +int auparse_timestamp_compare(au_event_t *e1, au_event_t *e2); + +.SH "DESCRIPTION" + +auparse_timestamp_compare compares the values of 2 timestamps. + +.SH "RETURN VALUE" + +Returns \-1, 0, or 1 respectively depending on whether e2 is less than, equal to, or greater than e1. + +.SH "SEE ALSO" + +.BR auparse_get_timestamp (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/aureport.8 b/framework/src/audit/docs/aureport.8 new file mode 100644 index 00000000..365f4188 --- /dev/null +++ b/framework/src/audit/docs/aureport.8 @@ -0,0 +1,131 @@ +.TH AUREPORT: "8" "Sept 2014" "Red Hat" "System Administration Utilities" +.SH NAME +aureport \- a tool that produces summary reports of audit daemon logs +.SH SYNOPSIS +.B aureport +.RI [ options ] +.SH DESCRIPTION +\fBaureport\fP is a tool that produces summary reports of the audit system logs. The aureport utility can also take input from stdin as long as the input is the raw log data. The reports have a column label at the top to help with interpretation of the various fields. Except for the main summary report, all reports have the audit event number. You can subsequently lookup the full event with ausearch \fB\-a\fP \fIevent number\fP. You may need to specify start & stop times if you get multiple hits. The reports produced by aureport can be used as building blocks for more complicated analysis. + +.SH OPTIONS +.TP +.BR \-au ,\ \-\-auth +Report about authentication attempts +.TP +.BR \-a ,\ \-\-avc +Report about avc messages +.TP +.BR \-\-comm +Report about commands run +.TP +.BR \-c ,\ \-\-config +Report about config changes +.TP +.BR \-cr ,\ \-\-crypto +Report about crypto events +.TP +.BR \-e ,\ \-\-event +Report about events +.TP +.BR \-f ,\ \-\-file +Report about files +.TP +.B \-\-failed +Only select failed events for processing in the reports. The default is both success and failed events. +.TP +.BR \-h ,\ \-\-host +Report about hosts +.TP +.BR \-\-help +Print brief command summary +.TP +.BR \-i ,\ \-\-interpret +Interpret numeric entities into text. For example, uid is converted to account name. The conversion is done using the current resources of the machine where the search is being run. If you have renamed the accounts, or don't have the same accounts on your machine, you could get misleading results. +.TP +.BR \-if ,\ \-\-input \ \fIfile\fP\ |\ \fIdirectory\fP +Use the given \fIfile\fP or \fIdirectory\fP instead of the logs. This is to aid analysis where the logs have been moved to another machine or only part of a log was saved. +.TP +.B \-\-input\-logs +Use the log file location from auditd.conf as input for analysis. This is needed if you are using aureport from a cron job. +.TP +.BR \-\-integrity +Report about integrity events +.TP +.BR \-k ,\ \-\-key +Report about audit rule keys +.TP +.BR \-l ,\ \-\-login +Report about logins +.TP +.BR \-m ,\ \-\-mods +Report about account modifications +.TP +.BR \-ma ,\ \-\-mac +Report about Mandatory Access Control (MAC) events +.TP +.BR \-n ,\ \-\-anomaly +Report about anomaly events. These events include NIC going into promiscuous mode and programs segfaulting. +.TP +.BR \-\-node \ \fInode-name\fP +Only select events originating from \fInode name\fP string for processing in the reports. The default is to include all nodes. Multiple nodes are allowed. +.TP +.BR \-nc ,\ \-\-no-config +Do not include the CONFIG_CHANGE event. This is particularly useful for the key report because audit rules have key labels in many cases. Using this option gets rid of these false positives. +.TP +.BR \-p ,\ \-\-pid +Report about processes +.TP +.BR \-r ,\ \-\-response +Report about responses to anomaly events +.TP +.BR \-s ,\ \-\-syscall +Report about syscalls +.TP +.B \-\-success +Only select successful events for processing in the reports. The default is both success and failed events. +.TP +.B \-\-summary +Run the summary report that gives a total of the elements of the main report. Not all reports have a summary. +.TP +.BR \-t ,\ \-\-log +This option will output a report of the start and end times for each log. +.TP +.BR \-\-tty +Report about tty keystrokes +.TP +.BR \-te ,\ \-\-end \ [\fIend-date\fP]\ [\fIend-time\fP] +Search for events with time stamps equal to or before the given end time. The format of end time depends on your locale. If the date is omitted, +.B today +is assumed. If the time is omitted, +.B now +is assumed. Use 24 hour clock time rather than AM or PM to specify time. An example date using the en_US.utf8 locale is 09/03/2009. An example of time is 18:00:00. The date format accepted is influenced by the LC_TIME environmental variable. + +You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP, \fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP, \fBthis\-year\fP. \fBToday\fP means starting now. \fBRecent\fP is 10 minutes ago. \fBYesterday\fP is 1 second after midnight the previous day. \fBThis\-week\fP means starting 1 second after midnight on day 0 of the week determined by your locale (see \fBlocaltime\fP). \fBWeek\-ago\fP means 1 second after midnight exactly 7 days ago. \fBThis\-month\fP means 1 second after midnight on day 1 of the month. \fBThis\-year\fP means the 1 second after midnight on the first day of the first month. +.TP +.BR \-tm ,\ \-\-terminal +Report about terminals +.TP +.BR \-ts ,\ \-\-start \ [\fIstart-date\fP]\ [\fIstart-time\fP] +Search for events with time stamps equal to or after the given end time. The format of end time depends on your locale. If the date is omitted, +.B today +is assumed. If the time is omitted, +.B midnight +is assumed. Use 24 hour clock time rather than AM or PM to specify time. An example date using the en_US.utf8 locale is 09/03/2009. An example of time is 18:00:00. The date format accepted is influenced by the LC_TIME environmental variable. + +You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP, \fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP, \fBthis\-year\fP. \fBToday\fP means starting at 1 second after midnight. \fBRecent\fP is 10 minutes ago. \fBYesterday\fP is 1 second after midnight the previous day. \fBThis\-week\fP means starting 1 second after midnight on day 0 of the week determined by your locale (see \fBlocaltime\fP). \fBWeek\-ago\fP means starting 1 second after midnight exactly 7 days ago. \fBThis\-month\fP means 1 second after midnight on day 1 of the month. \fBThis\-year\fP means the 1 second after midnight on the first day of the first month. +.TP +.BR \-u ,\ \-\-user +Report about users +.TP +.BR \-v ,\ \-\-version +Print the version and exit +.TP +.BR \-\-virt +Report about Virtualization events +.TP +.BR \-x ,\ \-\-executable +Report about executables + +.SH "SEE ALSO" +.BR ausearch (8), +.BR auditd (8). diff --git a/framework/src/audit/docs/ausearch-expression.5 b/framework/src/audit/docs/ausearch-expression.5 new file mode 100644 index 00000000..73549239 --- /dev/null +++ b/framework/src/audit/docs/ausearch-expression.5 @@ -0,0 +1,241 @@ +.TH "AUSEARCH-EXPRESSION" "5" "Feb 2008" "Red Hat" "Linux Audit" +.SH NAME +ausearch-expression \- audit search expression format + +.SH OVERVIEW +This man page describes the format of "ausearch expressions". +Parsing and evaluation of these expressions is provided by libauparse +and is common to applications that use this library. + +.SH LEXICAL STRUCTURE + +White space (ASCII space, tab and new-line characters) between tokens is +ignored. +The following tokens are recognized: + +.TP +Punctuation +.B ( ) \e + +.TP +Logical operators +.B ! && || + +.TP +Comparison operators +.B < <= == > >= !== i= i!= r= r!= + +.TP +Unquoted strings +Any non-empty sequence of ASCII letters, digits, and the +.B _ +symbol. + +.TP +Quoted strings +A sequence of characters surrounded by the +.B \(dq +quotes. +The +.B \e +character starts an escape sequence. +The only defined escape sequences are +.B \e\e +and \fB\e\(dq\fR. +The semantics of other escape sequences is undefined. + +.TP +Regexps +A sequence of characters surrounded by the +.B / +characters. +The +.B \e +character starts an escape sequence. +The only defined escape sequences are +.B \e\e +and \fB\e/\fR. +The semantics of other escape sequences is undefined. + +.PP +Anywhere an unquoted string is valid, a quoted string is valid as well, +and vice versa. +In particular, field names may be specified using quoted strings, +and field values may be specified using unquoted strings. + +.SH EXPRESSION SYNTAX + +The primary expression has one of the following forms: +.IP +.I field comparison-operator value + +.B \eregexp +.I string-or-regexp +.PP + +.I field +is either a string, +which specifies the first field with that name within the current audit record, +or the +.B \e +escape character followed by a string, +which specifies a virtual field with the specified name +(virtual fields are defined in a later section). + +.I field +is a string. +.I operator +specifies the comparison to perform + +.TP +.B r= r!= +Get the "raw" string of \fIfield\fR, +and compare it to \fIvalue\fR. +For fields in audit records, +the "raw" string is the exact string stored in the audit record +(with all escaping and unprintable character encoding left alone); +applications can read the "raw" string using +.BR auparse_get_field_str (3). +Each virtual field may define a "raw" string. +If +.I field +is not present or does not define a "raw" string, +the result of the comparison is +.B false +(regardless of the operator). + +.TP +.B i= i!= +Get the "interpreted" string of \fIfield\fR, +and compare it to \fIvalue\fR. +For fields in audit records, +the "interpreted" string is an "user-readable" interpretation of the field +value; +applications can read the "interpreted" string using +.BR auparse_interpret_field (3). +Each virtual field may define an "interpreted" string. +If +.I field +is not present or does not define an "interpreted" string, +the result of the comparison is +.B false +(regardless of the operator). + +.TP +.B < <= == > >= !== +Evaluate the "value" of \fIfield\fR, and compare it to \fIvalue\fR. +A "value" may be defined for any field or virtual field, +but no "value" is currently defined for any audit record field. +The rules of parsing \fIvalue\fR for comparing it with the "value" of +.I field +are specific for each \fIfield\fR. +If +.I field +is not present, +the result of the comparison is +.B false +(regardless of the operator). +If +.I field +does not define a "value", an error is reported when parsing the expression. +.PP + +In the special case of +.B \eregexp +\fIregexp-or-string\fR, +the current audit record is taken as a string +(without interpreting field values), +and matched against \fIregexp-or-string\fR. +.I regexp-or-string +is an extended regular expression, using a string or regexp token +(in other words, delimited by +.B \(dq +or \fB/\fR). + +If +.I E1 +and +.I E2 +are valid expressions, +then +.B ! +\fIE1\fR, +.I E1 +.B && +\fIE2\fR, and +.I E1 +.B || +.I E2 +are valid expressions as well, with the usual C semantics and evaluation +priorities. +Note that +.B ! +.I field op value +is interpreted as \fB!(\fIfield op value\fB)\fR, not as +\fB(!\fIfield\fB)\fI op value\fR. + +.SH VIRTUAL FIELDS + +The following virtual fields are defined: + +.TP +.B \etimestamp +The value is the timestamp of the current event. +.I value +must have the \fBts:\fIseconds\fR.\fImilli\fR format, where +.I seconds +and +.I milli +are decimal numbers specifying the seconds and milliseconds part of the +timestamp, respectively. + +.TP +.B \erecord_type +The value is the type of the current record. +.I value +is either the record type name, or a decimal number specifying the type. + +.SH SEMANTICS +The expression as a whole applies to a single record. +The expression is +.B true +for a specified event if it is +.B true +for any record associated with the event. + +.SH EXAMPLES + +As a demonstration of the semantics of handling missing fields, the following +expression is +.B true +if +.I field +is present: +.IP +.B (\fIfield\fB r= \(dq\(dq) || (\fIfield\fB r!= \(dq\(dq) +.PP +and the same expression surrounded by +.B !( +and +.B ) +is +.B true +if +.I field +is not present. + +.SH FUTURE DIRECTIONS +New escape sequences for quoted strings may be defined. + +For currently defined virtual fields that do not define a "raw" or +"interpreted" string, the definition may be added. +Therefore, don't rely on the fact +that comparing the "raw" or "interpreted" string of the field with any value +is \fBfalse\fR. + +New formats of value constants for the +.B \etimestamp +virtual field may be added. + +.SH AUTHOR +Miloslav Trmac diff --git a/framework/src/audit/docs/ausearch.8 b/framework/src/audit/docs/ausearch.8 new file mode 100644 index 00000000..c7b30314 --- /dev/null +++ b/framework/src/audit/docs/ausearch.8 @@ -0,0 +1,208 @@ +.TH AUSEARCH: "8" "Sept 2009" "Red Hat" "System Administration Utilities" +.SH NAME +ausearch \- a tool to query audit daemon logs +.SH SYNOPSIS +.B ausearch +.RI [ options ] +.SH DESCRIPTION +\fBausearch\fP is a tool that can query the audit daemon logs based for events based on different search criteria. The ausearch utility can also take input from stdin as long as the input is the raw log data. Each commandline option given forms an "and" statement. For example, searching with \fB\-m\fP and \fB\-ui\fP means return events that have both the requested type and match the user id given. An exception is the \fB\-n\fP option; multiple nodes are allowed in a search which will return any matching node. + +It should also be noted that each syscall excursion from user space into the kernel and back into user space has one event ID that is unique. Any auditable event that is triggered during this trip share this ID so that they may be correlated. + +Different parts of the kernel may add supplemental records. For example, an audit event on the syscall "open" will also cause the kernel to emit a PATH record with the file name. The ausearch utility will present all records that make up one event together. This could mean that even though you search for a specific kind of record, the resulting events may contain SYSCALL records. + +Also be aware that not all record types have the requested information. For example, a PATH record does not have a hostname or a loginuid. + +.SH OPTIONS +.TP +.BR \-a ,\ \-\-event \ \fIaudit-event-id\fP +Search for an event based on the given \fIevent ID\fP. Messages always start with something like msg=audit(1116360555.329:2401771). The event ID is the number after the ':'. All audit events that are recorded from one application's syscall have the same audit event ID. A second syscall made by the same application will have a different event ID. This way they are unique. +.TP +.BR \-\-arch \ \fICPU\fP +Search for events based on a specific CPU architecture. If you do not know the arch of your machine but you want to use the 32 bit syscall table and your machine supports 32 bits, you can also use +.B b32 +for the arch. The same applies to the 64 bit syscall table, you can use +.B b64. +The arch of your machine can be found by doing 'uname -m'. +.TP +.BR \-c ,\ \-\-comm \ \fIcomm-name\fP +Search for an event based on the given \fIcomm name\fP. The comm name is the executable's name from the task structure. +.TP +.BR \-\-debug +Write malformed events that are skipped to stderr. +.TP +.BR \-\-checkpoint \ \fIcheckpoint-file\fP +Checkpoint the output between successive invocations of ausearch such that only events not +previously output will print in subsequent invocations. + +An auditd event is made up of one or more records. When processing events, ausearch defines +events as either complete or in-complete. A complete event is either a single record event or +one whose event time occurred 2 seconds in the past compared to the event being currently +processed. + +A checkpoint is achieved by recording the last completed event output along with the device +number and inode of the file the last completed event appeared in \fIcheckpoint-file\fP. On a subsequent invocation, +ausearch will load this checkpoint data and as it processes the log files, it will discard all +complete events until it matches the checkpointed one. At this point, it will start +outputting complete events. + +Should the file or the last checkpointed event not be found, one of a number of errors will result and ausearch will terminate. See \fBEXIT STATUS\fP for detail. + +.TP +.BR \-e,\ \-\-exit \ \fIexit-code-or-errno\fP +Search for an event based on the given syscall \fIexit code or errno\fP. +.TP +.BR \-f ,\ \-\-file \ \fIfile-name\fP +Search for an event based on the given \fIfilename\fP. +.TP +.BR \-ga ,\ \-\-gid\-all \ \fIall-group-id\fP +Search for an event with either effective group ID or group ID matching the given \fIgroup ID\fP. +.TP +.BR \-ge ,\ \-\-gid\-effective \ \fIeffective-group-id\fP +Search for an event with the given \fIeffective group ID\fP or group name. +.TP +.BR \-gi ,\ \-\-gid \ \fIgroup-id\fP +Search for an event with the given \fIgroup ID\fP or group name. +.TP +.BR \-h ,\ \-\-help +Help +.TP +.BR \-hn ,\ \-\-host \ \fIhost-name\fP +Search for an event with the given \fIhost name\fP. The hostname can be either a hostname, fully qualified domain name, or numeric network address. No attempt is made to resolve numeric addresses to domain names or aliases. +.TP +.BR \-i ,\ \-\-interpret +Interpret numeric entities into text. For example, uid is converted to account name. The conversion is done using the current resources of the machine where the search is being run. If you have renamed the accounts, or don't have the same accounts on your machine, you could get misleading results. +.TP +.BR \-if ,\ \-\-input \ \fIfile-name\fP\ |\ \fIdirectory\fP +Use the given \fIfile\fP or \fIdirectory\fP instead of the logs. This is to aid analysis where the logs have been moved to another machine or only part of a log was saved. +.TP +.BR \-\-input\-logs +Use the log file location from auditd.conf as input for searching. This is needed if you are using ausearch from a cron job. +.TP +.BR \-\-just\-one +Stop after emitting the first event that matches the search criteria. +.TP +.BR \-k ,\ \-\-key \ \fIkey-string\fP +Search for an event based on the given \fIkey string\fP. +.TP +.BR \-l ,\ \-\-line\-buffered +Flush output on every line. Most useful when stdout is connected to a pipe and the default block buffering strategy is undesirable. May impose a performance penalty. +.TP +.BR \-m ,\ \-\-message \ \fImessage-type\fP\ |\ \fIcomma-sep-message-type-list\fP +Search for an event matching the given \fImessage type\fP. You may also enter a \fIcomma separated list of message types\fP. There is an \fBALL\fP message type that doesn't exist in the actual logs. It allows you to get all messages in the system. The list of valid messages types is long. The program will display the list whenever no message type is passed with this parameter. The message type can be either text or numeric. If you enter a list, there can be only commas and no spaces separating the list. +.TP +.BR \-n ,\ \-\-node \ \fInode-name\fP +Search for events originating from \fInode name\fP string. Multiple nodes are allowed, and if any nodes match, the event is matched. +.TP +.BR \-o ,\ \-\-object \ \fISE-Linux-context-string\fP +Search for event with \fItcontext\fP (object) matching the string. +.TP +.BR \-p ,\ \-\-pid \ \fIprocess-id\fP +Search for an event matching the given \fIprocess ID\fP. +.TP +.BR \-pp ,\ \-\-ppid \ \fIparent-process-id\fP +Search for an event matching the given \fIparent process ID\fP. +.TP +.BR \-r ,\ \-\-raw +Output is completely unformatted. This is useful for extracting records that can still be interpreted by audit tools. +.TP +.BR \-sc ,\ \-\-syscall \ \fIsyscall-name-or-value\fP +Search for an event matching the given \fIsyscall\fP. You may either give the numeric syscall value or the syscall name. If you give the syscall name, it will use the syscall table for the machine that you are using. +.TP +.BR \-se ,\ \-\-context \ \fISE-Linux-context-string\fP +Search for event with either \fIscontext\fP/subject or \fItcontext\fP/object matching the string. +.TP +.BR \-\-session \ \fILogin-Session-ID\fP +Search for events matching the given Login Session ID. This process attribute is set when a user logs in and can tie any process to a particular user login. +.TP +.BR \-su ,\ \-\-subject \ \fISE-Linux-context-string\fP +Search for event with \fIscontext\fP (subject) matching the string. +.TP +.BR \-sv ,\ \-\-success \ \fIsuccess-value\fP +Search for an event matching the given \fIsuccess value\fP. Legal values are +.B yes +and +.BR no . +.TP +.BR \-te ,\ \-\-end \ [\fIend-date\fP]\ [\fIend-time\fP] +Search for events with time stamps equal to or before the given end time. The format of end time depends on your locale. If the date is omitted, +.B today +is assumed. If the time is omitted, +.B now +is assumed. Use 24 hour clock time rather than AM or PM to specify time. An example date using the en_US.utf8 locale is 09/03/2009. An example of time is 18:00:00. The date format accepted is influenced by the LC_TIME environmental variable. + +You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP, \fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP, or \fBthis\-year\fP. \fBToday\fP means starting now. \fBRecent\fP is 10 minutes ago. \fBYesterday\fP is 1 second after midnight the previous day. \fBThis\-week\fP means starting 1 second after midnight on day 0 of the week determined by your locale (see \fBlocaltime\fP). \fBWeek\-ago\fP means 1 second after midnight exactly 7 days ago. \fBThis\-month\fP means 1 second after midnight on day 1 of the month. \fBThis\-year\fP means the 1 second after midnight on the first day of the first month. +.TP +.BR \-ts ,\ \-\-start \ [\fIstart-date\fP]\ [\fIstart-time\fP] +Search for events with time stamps equal to or after the given start time. The format of start time depends on your locale. If the date is omitted, +.B today +is assumed. If the time is omitted, +.B midnight +is assumed. Use 24 hour clock time rather than AM or PM to specify time. An example date using the en_US.utf8 locale is 09/03/2009. An example of time is 18:00:00. The date format accepted is influenced by the LC_TIME environmental variable. + +You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP, \fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP, \fBthis\-year\fP, or \fBcheckpoint\fP. \fBToday\fP means starting at 1 second after midnight. \fBRecent\fP is 10 minutes ago. \fBYesterday\fP is 1 second after midnight the previous day. \fBThis\-week\fP means starting 1 second after midnight on day 0 of the week determined by your locale (see \fBlocaltime\fP). \fBWeek\-ago\fP means starting 1 second after midnight exactly 7 days ago. \fBThis\-month\fP means 1 second after midnight on day 1 of the month. \fBThis\-year\fP means the 1 second after midnight on the first day of the first month. +.sp +\fBcheckpoint\fP means \fIausearch\fP will use the timestamp found within a valid checkpoint file ignoring the recorded inode, device, serial, node and event type also found within a checkpoint file. Essentially, this is the recovery action should an invocation of \fIausearch\fP with a checkpoint option fail with an exit status of 10, 11 or 12. It could be used in a shell script something like: +.sp +.in +5 +.nf +.na +ausearch --checkpoint /etc/audit/auditd_checkpoint.txt -i +_au_status=$? +if test ${_au_status} eq 10 -o ${_au_status} eq 11 -o ${_au_status} eq 12 +then + ausearch --checkpoint /etc/audit/auditd_checkpoint.txt --start checkpoint -i +fi +.ad +.fi +.in -5 +.TP +.BR \-tm ,\ \-\-terminal \ \fIterminal\fP +Search for an event matching the given \fIterminal\fP value. Some daemons such as cron and atd use the daemon name for the terminal. +.TP +.BR \-ua ,\ \-\-uid\-all \ \fIall-user-id\fP +Search for an event with either user ID, effective user ID, or login user ID (auid) matching the given \fIuser ID\fP. +.TP +.BR \-ue ,\ \-\-uid\-effective \ \fIeffective-user-id\fP +Search for an event with the given \fIeffective user ID\fP. +.TP +.BR \-ui ,\ \-\-uid \ \fIuser-id\fP +Search for an event with the given \fIuser ID\fP. +.TP +.BR \-ul ,\ \-\-loginuid \ \fIlogin-id\fP +Search for an event with the given \fIlogin user ID\fP. All entry point programs that are pamified need to be configured with pam_loginuid required for the session for searching on loginuid (auid) to be accurate. +.TP +.BR \-uu ,\ \-\-uuid \ \fIguest-uuid\fP +Search for an event with the given \fIguest UUID\fP. +.TP +.BR \-v ,\ \-\-version +Print the version and exit +.TP +.BR \-vm ,\ \-\-vm-name \ \fIguest-name\fP +Search for an event with the given \fIguest name\fP. +.TP +.BR \-w ,\ \-\-word +String based matches must match the whole word. This category of matches include: filename, hostname, terminal, and SE Linux context. +.TP +.BR \-x ,\ \-\-executable \ \fIexecutable\fP +Search for an event matching the given \fIexecutable\fP name. + +.SH "EXIT STATUS" +.TP 5 +0 +if OK, +.TP +1 +if nothing found, or argument errors or minor file acces/read errors, +.TP +10 +invalid checkpoint data found in checkpoint file, +.TP +11 +checkpoint processing error +.TP +12 +checkpoint event not found in matching log file +.SH "SEE ALSO" +.BR auditd (8), +.BR pam_loginuid (8). diff --git a/framework/src/audit/docs/ausearch_add_expression.3 b/framework/src/audit/docs/ausearch_add_expression.3 new file mode 100644 index 00000000..c3c17c9d --- /dev/null +++ b/framework/src/audit/docs/ausearch_add_expression.3 @@ -0,0 +1,71 @@ +.TH "AUSEARCH_ADD_expression" "3" "Feb 2008" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_expression \- build up search expression +.SH "SYNOPSIS" +.B #include + +\fBint ausearch_add_expression(auparse_state_t *\fIau\fB, +const char *\fIexpression\fB, char **\fIerror\fB, ausearch_rule_t \fIhow\fB);\fR + +.SH "DESCRIPTION" + +.B ausearch_add_item +adds an expression to the current audit search expression. +The search conditions can then be used to scan logs, files, or buffers +for something of interest. +The +.I expression +parameter contains an expression, as specified in +.BR ausearch\-expression (5). + +The +.I how +parameter determines +how this search expression will affect the existing search expression, +if one is already defined. +The possible values are: +.RS +.TP +.I AUSEARCH_RULE_CLEAR +Clear the current search expression, if any, +and use only this search expression. +.TP +.I AUSEARCH_RULE_OR +If a search expression +.I E +is already configured, +replace it by \fB(\fIE\fB || \fIthis_search_expression\fB)\fR. +.TP +.I AUSEARCH_RULE_AND +If a search expression +.I E +is already configured, +replace it by \fB(\fIE\fB && \fIthis_search_expression\fB)\fR. +.RE + +.SH "RETURN VALUE" + +If successful, +.B ausearch_add_expression +returns 0. +Otherwise, it returns \-1, sets +.B errno +and it may set \fB*\fIerror\fR to an error message; +the caller must free the error message using +.BR free (3). +If an error message is not available or can not be allocated, \fB*\fIerror\fR +is set to \fBNULL\fR. + +.SH "SEE ALSO" + +.BR ausearch_add_item (3), +.BR ausearch_add_interpreted_item (3), +.BR ausearch_add_timestamp_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR ausearch\-expression (5). + +.SH AUTHOR +Miloslav Trmac diff --git a/framework/src/audit/docs/ausearch_add_interpreted_item.3 b/framework/src/audit/docs/ausearch_add_interpreted_item.3 new file mode 100644 index 00000000..217ab707 --- /dev/null +++ b/framework/src/audit/docs/ausearch_add_interpreted_item.3 @@ -0,0 +1,60 @@ +.TH "AUSEARCH_ADD_INTERPRETED_ITEM" "3" "Nov 2007" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_interpreted_item \- build up search rule +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_add_interpreted_item(auparse_state_t *au, const char *field, const char *op, const char *value, ausearch_rule_t how); + +.SH "DESCRIPTION" + +ausearch_add_interpreted_item adds one search condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The field value is the field name that the value will be checked for. The op variable describes what kind of check is to be done. Legal op values are: + +.RS +.TP +.I "exists" + just check that a field name exists +.TP +.I "=" + locate the field name and check that the value associated with it is equal to the value given in this rule. +.TP +.I "!=" + locate the field name and check that the value associated with it is NOT equal to the value given in this rule. +.RE + +The value parameter is compared to the interpreted field value (the value that would be returned by \fBauparse_interpret_field\fR(3)). + +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: +.RS +.TP +.I AUSEARCH_RULE_CLEAR +Clear the current search expression, if any, and use only this search condition. +.TP +.I AUSEARCH_RULE_OR +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. +.TP +.I AUSEARCH_RULE_AND +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. +.RE + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR ausearch_add_expression (3), +.BR ausearch_add_item (3), +.BR ausearch_add_timestamp_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR ausearch\-expression (5). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/ausearch_add_item.3 b/framework/src/audit/docs/ausearch_add_item.3 new file mode 100644 index 00000000..9193267c --- /dev/null +++ b/framework/src/audit/docs/ausearch_add_item.3 @@ -0,0 +1,60 @@ +.TH "AUSEARCH_ADD_ITEM" "3" "Feb 2012" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_item \- build up search rule +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_add_item(auparse_state_t *au, const char *field, const char *op, const char *value, ausearch_rule_t how); + +.SH "DESCRIPTION" + +ausearch_add_item adds one search condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The field value is the field name that the value will be checked for. The op variable describes what kind of check is to be done. Legal op values are: + +.RS +.TP +.I "exists" + just check that a field name exists +.TP +.I "=" + locate the field name and check that the value associated with it is equal to the value given in this rule. +.TP +.I "!=" + locate the field name and check that the value associated with it is NOT equal to the value given in this rule. +.RE + +The value parameter is compared to the uninterpreted field value. If you are trying to match against a field who's type is AUPARSE_TYPE_ESCAPED, you will want to use the ausearch_add_interpreted_item() function instead. + +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: +.RS +.TP +.I AUSEARCH_RULE_CLEAR +Clear the current search expression, if any, and use only this search condition. +.TP +.I AUSEARCH_RULE_OR +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. +.TP +.I AUSEARCH_RULE_AND +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. +.RE + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR ausearch_add_expression (3), +.BR ausearch_add_interpreted_item (3), +.BR ausearch_add_timestamp_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR ausearch\-expression (5). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/ausearch_add_regex.3 b/framework/src/audit/docs/ausearch_add_regex.3 new file mode 100644 index 00000000..b37b6571 --- /dev/null +++ b/framework/src/audit/docs/ausearch_add_regex.3 @@ -0,0 +1,31 @@ +.TH "AUSEARCH_ADD_REGEX" "3" "Sept 2007" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_regex \- use regular expression search rule +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_add_regex(auparse_state_t *au, const char *expr); + +.SH "DESCRIPTION" + +ausearch_add_regex adds one search condition based on a regular expression to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The regular expression follows the posix extended regular expression conventions, and is matched against the full record (without interpreting field values). + +If an existing search expression +.I E +is already defined, +this function replaces it by \fB(\fIE\fB && \fIthis_regexp\fB)\fR. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR ausearch_add_expression (3), +.BR ausearch_add_item (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR regcomp (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/ausearch_add_timestamp_item.3 b/framework/src/audit/docs/ausearch_add_timestamp_item.3 new file mode 100644 index 00000000..091d4262 --- /dev/null +++ b/framework/src/audit/docs/ausearch_add_timestamp_item.3 @@ -0,0 +1,57 @@ +.TH "AUSEARCH_ADD_TIMESTAMP_ITEM" "3" "Aug 2014" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_timestamp_item \- build up search rule +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_add_timestamp_item(auparse_state_t *au, const char *op, time_t sec, unsigned milli, ausearch_rule_t how) + +.SH "DESCRIPTION" + +ausearch_add_timestamp_item adds an event time condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The op parameter specifies the desired comparison. Legal op values are \fI<\fR, \fI<=\fR, \fI>=\fR, \fI>\fR and \fI=\fR. The left operand of the comparison operator is the timestamp of the examined event, the right operand is specified by the sec and milli parameters. + +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: +.RS +.TP +.I AUSEARCH_RULE_CLEAR +Clear the current search expression, if any, and use only this search condition. +.TP +.I AUSEARCH_RULE_OR +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. +.TP +.I AUSEARCH_RULE_AND +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. +.RE + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH APPLICATION USAGE + +Use +.BR ausearch_add_item (3) +and +.BR ausearch_add_interpreted_item (3) +to add conditions that check audit record fields. +Use +.BR ausearch_add_expression (3) +to add complex search expressions using a single function call. + +.SH "SEE ALSO" + +.BR ausearch_add_expression (3), +.BR ausearch_add_item (3), +.BR ausearch_add_interpreted_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR ausearch\-expression (5). + +.SH AUTHOR +Miloslav Trmac diff --git a/framework/src/audit/docs/ausearch_add_timestamp_item_ex.3 b/framework/src/audit/docs/ausearch_add_timestamp_item_ex.3 new file mode 100644 index 00000000..caa0114a --- /dev/null +++ b/framework/src/audit/docs/ausearch_add_timestamp_item_ex.3 @@ -0,0 +1,57 @@ +.TH "AUSEARCH_ADD_TIMESTAMP_ITEM_EX" "3" "Aug 2014" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_add_timestamp_item_ex \- build up search rule +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_add_timestamp_item_ex(auparse_state_t *au, const char *op, time_t sec, unsigned milli, unsigned serial, ausearch_rule_t how) + +.SH "DESCRIPTION" + +ausearch_add_timestamp_item adds an event time condition to the current audit search expression. The search conditions can then be used to scan logs, files, or buffers for something of interest. The op parameter specifies the desired comparison. Legal op values are \fI<\fR, \fI<=\fR, \fI>=\fR, \fI>\fR and \fI=\fR. The left operand of the comparison operator is the timestamp of the examined event, the right operand is specified by the sec, milli, and serial parameters. + +The how value determines how this search condition will affect the existing search expression if one is already defined. The possible values are: +.RS +.TP +.I AUSEARCH_RULE_CLEAR +Clear the current search expression, if any, and use only this search condition. +.TP +.I AUSEARCH_RULE_OR +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB || \fIthis_search_condition\fB)\fR. +.TP +.I AUSEARCH_RULE_AND +If a search expression +.I E +is already configured, replace it by \fB(\fIE\fB && \fIthis_search_condition\fB)\fR. +.RE + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH APPLICATION USAGE + +Use +.BR ausearch_add_item (3) +and +.BR ausearch_add_interpreted_item (3) +to add conditions that check audit record fields. +Use +.BR ausearch_add_expression (3) +to add complex search expressions using a single function call. + +.SH "SEE ALSO" + +.BR ausearch_add_expression (3), +.BR ausearch_add_item (3), +.BR ausearch_add_interpreted_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3), +.BR ausearch\-expression (5). + +.SH AUTHOR +Miloslav Trmac diff --git a/framework/src/audit/docs/ausearch_clear.3 b/framework/src/audit/docs/ausearch_clear.3 new file mode 100644 index 00000000..1f8ad20a --- /dev/null +++ b/framework/src/audit/docs/ausearch_clear.3 @@ -0,0 +1,23 @@ +.TH "AUSEARCH_CLEAR" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_clear \- clear search parameters +.SH "SYNOPSIS" +.B #include +.sp +void ausearch_clear(auparse_state_t *au); + +.SH "DESCRIPTION" + +ausearch_clear clears any search parameters stored in the parser instance and frees memory associated with it. + +.SH "RETURN VALUE" + +None. + +.SH "SEE ALSO" + +.BR ausearch_add_item (3), +.BR ausearch_add_regex (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/ausearch_next_event.3 b/framework/src/audit/docs/ausearch_next_event.3 new file mode 100644 index 00000000..57f11efd --- /dev/null +++ b/framework/src/audit/docs/ausearch_next_event.3 @@ -0,0 +1,24 @@ +.TH "AUSEARCH_NEXT_EVENT" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME +ausearch_next_event \- find the next event that meets search criteria +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_next_event(auparse_state_t *au); + +.SH "DESCRIPTION" + +ausearch_next_event will scan the input source and evaluate whether any record in an event contains the data being searched for. Evaluation is done at the record level. + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs, 0 if no matches, and 1 for success. + +.SH "SEE ALSO" + +.BR ausearch_add_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_set_stop (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/ausearch_set_stop.3 b/framework/src/audit/docs/ausearch_set_stop.3 new file mode 100644 index 00000000..627bb822 --- /dev/null +++ b/framework/src/audit/docs/ausearch_set_stop.3 @@ -0,0 +1,37 @@ +.TH "AUSEARCH_SET_STOP" "3" "Feb 2007" "Red Hat" "Linux Audit API" +.SH NAME + ausearch_set_stop \- set the cursor position +.SH "SYNOPSIS" +.B #include +.sp +int ausearch_set_stop(auparse_state_t *au, austop_t where); + +.SH "DESCRIPTION" + +ausearch_set_stop determines where the internal cursor will stop when a search condition is met. The possible values are: + +.RS +.TP +.I AUSEARCH_STOP_EVENT +This one repositions the cursors to the first field of the first record of the event containing the items searched for. +.TP +.I AUSEARCH_STOP_RECORD +This one repositions the cursors to the first field of the record containing the items searched for. +.TP +.I AUSEARCH_STOP_FIELD +This one simply stops on the current field when the evaluation of the rules becomes true. +.RE + +.SH "RETURN VALUE" + +Returns \-1 if an error occurs; otherwise, 0 for success. + +.SH "SEE ALSO" + +.BR ausearch_add_item (3), +.BR ausearch_add_regex (3), +.BR ausearch_clear (3), +.BR ausearch_next_event (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/autrace.8 b/framework/src/audit/docs/autrace.8 new file mode 100644 index 00000000..36a62248 --- /dev/null +++ b/framework/src/audit/docs/autrace.8 @@ -0,0 +1,38 @@ +.TH AUTRACE: "8" "Jan 2007" "Red Hat" "System Administration Utilities" +.SH NAME +autrace \- a program similar to strace +.SH SYNOPSIS +.B autrace +.I program +.RB [ \-r ] +.RI [ program-args ]... +.SH DESCRIPTION +\fBautrace\fP is a program that will add the audit rules to trace a process similar to strace. It will then execute the \fIprogram\fP passing \fIarguments\fP to it. The resulting audit information will be in the audit logs if the audit daemon is running or syslog. This command deletes all audit rules prior to executing the target program and after executing it. As a safety precaution, it will not run unless all rules are deleted with +.B auditctl +prior to use. +.SH OPTIONS +.TP +.B \-r +Limit syscalls collected to ones needed for analyzing resource usage. This could help people doing threat modeling. This saves space in logs. +.SH "EXAMPLES" +The following illustrates a typical session: + +.nf +.B autrace /bin/ls /tmp +.B ausearch \-\-start recent \-p 2442 \-i +.fi + +and for resource usage mode: + +.nf +.B autrace \-r /bin/ls +.B ausearch \-\-start recent \-p 2450 \-\-raw | aureport \-\-file \-\-summary +.B ausearch \-\-start recent \-p 2450 \-\-raw | aureport \-\-host \-\-summary +.fi + +.SH "SEE ALSO" +.BR ausearch (8), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/get_auditfail_action.3 b/framework/src/audit/docs/get_auditfail_action.3 new file mode 100644 index 00000000..ee6df4d2 --- /dev/null +++ b/framework/src/audit/docs/get_auditfail_action.3 @@ -0,0 +1,79 @@ +.\" Copyright (C) 2006 HP +.\" This file is distributed according to the GNU General Public License. +.\" See the file COPYING in the top level source directory for details. +.de Sh \" Subsection +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Ip \" List item +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.TH "GET_AUDITFAIL_ACTION" 3 "2006-7-10" "Linux 2.7" "Linux Programmer's Manual" +.SH NAME +get_auditfail_action \- Get failure_action tunable value +.SH "SYNOPSIS" +.ad l +.hy 0 + +#include +.sp +.HP 19 +int\ \fBget_auditfail_action\fR\ (int *\fIfailmode\fR); +.ad +.hy + +.SH "DESCRIPTION" + +.PP +This function gets the failure_action tunable value stored in \fB/etc/libaudit.conf\fR. \fBget_auditfail_action\fR should be called after an \fBaudit_open\fR call returns an error to see what action the admin prefers. + +.PP +The failure_action value found in \fB/etc/libaudit.conf\fR is copied into the \fIfailmode\fR argument upon function return. This value should then be used by the calling application to determine what action should be taken when the audit subsystem is unavailable. + +.SH "RETURN VALUE" + +.PP +Upon success, \fBget_auditfail_action\fR returns a zero, and the \fIfailmode\fR argument will hold the failure_action value. The possible values for failure_action are: FAIL_IGNORE (0), FAIL_LOG (1), and FAIL_TERMINATE (2). Upon failure, \fBget_auditfail_action\fR returns a return code of one. + +.SH "ERRORS" + +.PP +An error is returned if there is an error reading \fB/etc/libaudit.conf\fR or if the failure_action tunable is not found in the file. + +.SH "EXAMPLES" + +.PP + /* Sample code */ + auditfail_t failmode; + + if ((fd = audit_open() ) < 0 ) { + fprintf (stderr, "Cannot open netlink audit socket"); + + /* Get the failure_action */ + if ((rc = get_auditfail_action(&failmode)) == 0) { + if (failmode == FAIL_LOG) + fprintf (stderr, "Audit subsystem unavailable"); + else if (failmode == FAIL_TERMINATE) + exit (1); + /* If failmode == FAIL_IGNORE, do nothing */ + } + } + +.SH "SEE ALSO" + +.BR audit_open (3), +.BR auditd (8). + +.SH AUTHOR +Lisa M. Smith. diff --git a/framework/src/audit/docs/libaudit.conf.5 b/framework/src/audit/docs/libaudit.conf.5 new file mode 100644 index 00000000..945f8145 --- /dev/null +++ b/framework/src/audit/docs/libaudit.conf.5 @@ -0,0 +1,25 @@ +.TH LIBAUDIT.CONF: "5" "Oct 2009" "Red Hat" "System Administration Utilities" +.SH NAME +libaudit.conf \- libaudit configuration file +.SH DESCRIPTION +The file +.I /etc/libaudit.conf +contains configuration information for user space applications that link to libaudit. The applications are responsible for querrying the settings in this file and obeying the admin's preferences. This file contains one configuration keyword per line, an equal sign, and then followed by appropriate configuration information. The keywords recognized are: +.IR failure_action ". +These keywords are described below. + +.TP +.I failure_action +This keyword specifies what action the admin wishes a user space application to take when there is a failure to send an audit event to the kernel. The possible values are: +.IR IGNORE + - meaning do nothing, +.IR LOG +- write to syslog the inability to send an audit event, and +.I TERMINATE +- the user space application should exit. + +.SH "SEE ALSO" +.BR get_auditfail_action (3). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/docs/set_aumessage_mode.3 b/framework/src/audit/docs/set_aumessage_mode.3 new file mode 100644 index 00000000..abeafc75 --- /dev/null +++ b/framework/src/audit/docs/set_aumessage_mode.3 @@ -0,0 +1,56 @@ +.\" Copyright (C) 2004 IBM +.\" This file is distributed according to the GNU General Public License. +.\" See the file COPYING in the top level source directory for details. +.de Sh \" Subsection +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Ip \" List item +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.TH "SET_MESSAGE_MODE" 3 "2004-12-01" "Linux 2.6" "Linux Programmer's Manual" +.SH NAME +set_message_mode \- Sets the message mode +.SH "SYNOPSIS" +.ad l +.hy 0 + +#include +.sp +.HP 23 +void\ \fBset_message_mode\fR\ (message_t\ \fImode\fR); +.ad +.hy + +.SH "DESCRIPTION" + +.PP +\fBset_message_mode\fR sets the location where informational messages are sent. If \fImode\fR=0 (default), then informational messages are sent to stderr. If \fImode\fR=1, then informational messages are sent to syslog. + +.SH "EXAMPLE" + +.nf + +/* Sample code */ +set_message_mode(MSG_SYSLOG) + +.fi + +.SH "SEE ALSO" + +.BR auditd (8), +.BR audit_open (3). + +.SH AUTHOR +Debora Velarde. diff --git a/framework/src/audit/docs/zos-remote.conf.5 b/framework/src/audit/docs/zos-remote.conf.5 new file mode 100644 index 00000000..2ffd5b85 --- /dev/null +++ b/framework/src/audit/docs/zos-remote.conf.5 @@ -0,0 +1,69 @@ +.\" Copyright (c) International Business Machines Corp., 2007 +.\" +.\" This program is free software; you can redistribute it and/or +.\" modify it under the terms of the GNU General Public License as +.\" published by the Free Software Foundation; either version 2 of +.\" the License, or (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +.\" the GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 59 Temple Place, Suite 330, Boston, +.\" MA 02111-1307 USA +.\" +.\" Changelog: +.\" 2007-10-06, created by Klaus Heinrich Kiwi +.\" +.TH ZOS\-REMOTE.CONF 5 "Oct 2007" "IBM" "System Administration Utilities" +.SH NAME +zos\-remote.conf \- the audisp-racf plugin configuration file +.SH DESCRIPTION +.B zos\-remote.conf +controls the configuration for the +.BR audispd\-zos\-remote (8) +Audit dispatcher plugin. The default location for this file is +.IR /etc/audisp/zos\-remote.conf , +however, a different file can be specified as the first argument to the +.B audispd\-zos\-remote +plugin. See +.BR audispd\-zos\-remote (8) +and +.BR auditd (8). +The options available are as follows: +.TP +.I server +This is the IBM z/OS ITDS server hostname or IP address +.TP +.I port +The port number where ITDS is running on the z/OS server. Default is 389 (ldap port) +.TP +.I user +The z/OS RACF user ID which the audispd\-zos\-remote plugin will use to perform Remote Audit requests. This user needs READ access to FACILITY Class resource IRR.LDAP.REMOTE.AUDIT (See +.BR audispd\-zos\-remote (8)). +.TP +.I password +The password associated the the z/OS user ID configured above. +.TP +.I timeout +The number in seconds that +.B audispd\-zos\-remote +plugin will wait before giving up in connection attempts and event submissions. The default value is 15 +.TP +.I q_depth +The +.B audispd\-zos\-remote +plugin will queue inputed events to the maximum of +.I q_depth +events while trying to submit those remotely. This can handle burst of events or in case of a slow network connection. However, the +.B audispd\-zos\-remote +plugin will drop events in case the queue is full. The default queue depth is 64 - Increase this value in case you are experiencing event drop due to full queue +.RB ( audispd\-zos\-remote +will log this to syslog). +.SH "SEE ALSO" +.BR audispd\-zos\-remote (8) +.SH AUTHOR +Klaus Heinrich Kiwi diff --git a/framework/src/audit/init.d/Makefile.am b/framework/src/audit/init.d/Makefile.am new file mode 100644 index 00000000..521dd1d0 --- /dev/null +++ b/framework/src/audit/init.d/Makefile.am @@ -0,0 +1,82 @@ +# Makefile.am-- +# Copyright 2004-07,2012-13 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.rej *.orig +EXTRA_DIST = auditd.init auditd.service auditd.sysconfig auditd.conf \ + audit.rules auditd.cron libaudit.conf audispd.conf auditd.condrestart \ + auditd.restart auditd.resume auditd.rotate auditd.stop augenrules +libconfig = libaudit.conf +dispconfig = audispd.conf +dispconfigdir = $(sysconfdir)/audisp +if ENABLE_SYSTEMD +initdir = /usr/lib/systemd/system +legacydir = $(libexecdir)/initscripts/legacy-actions/auditd +else +initdir = $(sysconfdir)/rc.d/init.d +sysconfigdir = $(sysconfdir)/sysconfig +endif + +auditdir = $(sysconfdir)/audit +auditrdir = $(auditdir)/rules.d +dist_audit_DATA = auditd.conf +dist_auditr_DATA = audit.rules +sbin_SCRIPTS = augenrules + +install-data-hook: + $(INSTALL_DATA) -D -m 640 ${srcdir}/${dispconfig} ${DESTDIR}${dispconfigdir} + $(INSTALL_DATA) -D -m 640 ${srcdir}/${libconfig} ${DESTDIR}${sysconfdir} +if ENABLE_SYSTEMD +else + $(INSTALL_DATA) -D -m 640 ${srcdir}/auditd.sysconfig ${DESTDIR}${sysconfigdir}/auditd +endif + +install-exec-hook: +if ENABLE_SYSTEMD + mkdir -p ${DESTDIR}${initdir} + mkdir -p ${DESTDIR}${legacydir} + $(INSTALL_SCRIPT) -D -m 640 ${srcdir}/auditd.service ${DESTDIR}${initdir} + $(INSTALL_SCRIPT) -D -m 750 ${srcdir}/auditd.rotate ${DESTDIR}${legacydir}/rotate + $(INSTALL_SCRIPT) -D -m 750 ${srcdir}/auditd.resume ${DESTDIR}${legacydir}/resume + $(INSTALL_SCRIPT) -D -m 750 ${srcdir}/auditd.stop ${DESTDIR}${legacydir}/stop + $(INSTALL_SCRIPT) -D -m 750 ${srcdir}/auditd.restart ${DESTDIR}${legacydir}/restart + $(INSTALL_SCRIPT) -D -m 750 ${srcdir}/auditd.condrestart ${DESTDIR}${legacydir}/condrestart +else + $(INSTALL_SCRIPT) -D ${srcdir}/auditd.init ${DESTDIR}${initdir}/auditd +endif + chmod 0750 $(DESTDIR)$(sbindir)/augenrules + + +uninstall-hook: + rm ${DESTDIR}${dispconfigdir}/${dispconfig} + rm ${DESTDIR}${sysconfdir}/${libconfig} +if ENABLE_SYSTEMD + rm ${DESTDIR}${initdir}/auditd.service + rm ${DESTDIR}${legacydir}/rotate + rm ${DESTDIR}${legacydir}/resume + rm ${DESTDIR}${legacydir}/stop + rm ${DESTDIR}${legacydir}/restart + rm ${DESTDIR}${legacydir}/condrestart +else + rm ${DESTDIR}${sysconfigdir}/auditd + rm ${DESTDIR}${initdir}/auditd +endif + diff --git a/framework/src/audit/init.d/audispd.conf b/framework/src/audit/init.d/audispd.conf new file mode 100644 index 00000000..ee50e5b3 --- /dev/null +++ b/framework/src/audit/init.d/audispd.conf @@ -0,0 +1,12 @@ +# +# This file controls the configuration of the audit event +# dispatcher daemon, audispd. +# + +q_depth = 150 +overflow_action = SYSLOG +priority_boost = 4 +max_restarts = 10 +name_format = HOSTNAME +#name = mydomain + diff --git a/framework/src/audit/init.d/audit.rules b/framework/src/audit/init.d/audit.rules new file mode 100644 index 00000000..479ff470 --- /dev/null +++ b/framework/src/audit/init.d/audit.rules @@ -0,0 +1,14 @@ +# This file contains the auditctl rules that are loaded +# whenever the audit daemon is started via the initscripts. +# The rules are simply the parameters that would be passed +# to auditctl. + +# First rule - delete all +-D + +# Increase the buffers to survive stress events. +# Make this bigger for busy systems +-b 320 + +# Feel free to add below this line. See auditctl man page + diff --git a/framework/src/audit/init.d/auditd.condrestart b/framework/src/audit/init.d/auditd.condrestart new file mode 100644 index 00000000..efbaaa85 --- /dev/null +++ b/framework/src/audit/init.d/auditd.condrestart @@ -0,0 +1,7 @@ +#!/bin/sh +# Helper script to provide legacy auditd service options not +# directly supported by systemd. + +/usr/libexec/initscripts/legacy-actions/auditd/restart +RETVAL="$?" +exit $RETVAL diff --git a/framework/src/audit/init.d/auditd.conf b/framework/src/audit/init.d/auditd.conf new file mode 100644 index 00000000..fdc93f0e --- /dev/null +++ b/framework/src/audit/init.d/auditd.conf @@ -0,0 +1,32 @@ +# +# This file controls the configuration of the audit daemon +# + +log_file = /var/log/audit/audit.log +log_format = RAW +log_group = root +priority_boost = 4 +flush = INCREMENTAL +freq = 20 +num_logs = 5 +disp_qos = lossy +dispatcher = /sbin/audispd +name_format = NONE +##name = mydomain +max_log_file = 6 +max_log_file_action = ROTATE +space_left = 75 +space_left_action = SYSLOG +action_mail_acct = root +admin_space_left = 50 +admin_space_left_action = SUSPEND +disk_full_action = SUSPEND +disk_error_action = SUSPEND +##tcp_listen_port = +tcp_listen_queue = 5 +tcp_max_per_addr = 1 +##tcp_client_ports = 1024-65535 +tcp_client_max_idle = 0 +enable_krb5 = no +krb5_principal = auditd +##krb5_key_file = /etc/audit/audit.key diff --git a/framework/src/audit/init.d/auditd.cron b/framework/src/audit/init.d/auditd.cron new file mode 100644 index 00000000..7b898697 --- /dev/null +++ b/framework/src/audit/init.d/auditd.cron @@ -0,0 +1,14 @@ +#!/bin/sh + +########## +# This script can be installed to get a daily log rotation +# based on a cron job. +########## + +/sbin/service auditd rotate +EXITVALUE=$? +if [ $EXITVALUE != 0 ]; then + /usr/bin/logger -t auditd "ALERT exited abnormally with [$EXITVALUE]" +fi +exit 0 + diff --git a/framework/src/audit/init.d/auditd.init b/framework/src/audit/init.d/auditd.init new file mode 100755 index 00000000..ccf8afb1 --- /dev/null +++ b/framework/src/audit/init.d/auditd.init @@ -0,0 +1,175 @@ +#!/bin/bash +# +# auditd This starts and stops auditd +# +# chkconfig: 2345 11 88 +# description: This starts the Linux Auditing System Daemon, \ +# which collects security related events in a dedicated \ +# audit log. If this daemon is turned off, audit events \ +# will be sent to syslog. +# +# processname: /sbin/auditd +# config: /etc/sysconfig/auditd +# config: /etc/audit/auditd.conf +# pidfile: /var/run/auditd.pid +# +# Return values according to LSB for all commands but status: +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess argument(s) +# 3 - unimplemented feature (e.g. "reload") +# 4 - insufficient privilege +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# + + +PATH=/sbin:/bin:/usr/bin:/usr/sbin +prog="auditd" + +# Source function library. +. /etc/init.d/functions + +# Allow anyone to run status +if [ "$1" = "status" ] ; then + status $prog + RETVAL=$? + exit $RETVAL +fi + +# Check that we are root ... so non-root users stop here +test $EUID = 0 || exit 4 + +# Check config +test -f /etc/sysconfig/auditd && . /etc/sysconfig/auditd + +RETVAL=0 + +start(){ + test -x /sbin/auditd || exit 5 + test -f /etc/audit/auditd.conf || exit 6 + + echo -n $"Starting $prog: " + +# Localization for auditd is controlled in /etc/synconfig/auditd + if [ -z "$AUDITD_LANG" -o "$AUDITD_LANG" = "none" -o "$AUDITD_LANG" = "NONE" ]; then + unset LANG LC_TIME LC_ALL LC_MESSAGES LC_NUMERIC LC_MONETARY LC_COLLATE + else + LANG="$AUDITD_LANG" + LC_TIME="$AUDITD_LANG" + LC_ALL="$AUDITD_LANG" + LC_MESSAGES="$AUDITD_LANG" + LC_NUMERIC="$AUDITD_LANG" + LC_MONETARY="$AUDITD_LANG" + LC_COLLATE="$AUDITD_LANG" + export LANG LC_TIME LC_ALL LC_MESSAGES LC_NUMERIC LC_MONETARY LC_COLLATE + fi + unset HOME MAIL USER USERNAME + daemon $prog "$EXTRAOPTIONS" + RETVAL=$? + echo + if test $RETVAL = 0 ; then + touch /var/lock/subsys/auditd + # Prepare the default rules + if test x"$USE_AUGENRULES" != "x" ; then + if test "`echo $USE_AUGENRULES | tr 'NO' 'no'`" != "no" + then + test -d /etc/audit/rules.d && /sbin/augenrules + fi + fi + # Load the default rules + test -f /etc/audit/audit.rules && /sbin/auditctl -R /etc/audit/audit.rules >/dev/null + fi + return $RETVAL +} + +stop(){ + echo -n $"Stopping $prog: " + killproc $prog + RETVAL=$? + echo + rm -f /var/lock/subsys/auditd + # Remove watches so shutdown works cleanly + if test x"$AUDITD_CLEAN_STOP" != "x" ; then + if test "`echo $AUDITD_CLEAN_STOP | tr 'NO' 'no'`" != "no" + then + /sbin/auditctl -D >/dev/null + fi + fi + if test x"$AUDITD_STOP_DISABLE" != "x" ; then + if test "`echo $AUDITD_STOP_DISABLE | tr 'NO' 'no'`" != "no" + then + /sbin/auditctl -e 0 >/dev/null + fi + fi + return $RETVAL +} + +reload(){ + test -f /etc/audit/auditd.conf || exit 6 + echo -n $"Reloading configuration: " + killproc $prog -HUP + RETVAL=$? + echo + return $RETVAL +} + +rotate(){ + echo -n $"Rotating logs: " + killproc $prog -USR1 + RETVAL=$? + echo + return $RETVAL +} + +resume(){ + echo -n $"Resuming logging: " + killproc $prog -USR2 + RETVAL=$? + echo + return $RETVAL +} + +restart(){ + test -f /etc/audit/auditd.conf || exit 6 + stop + start +} + +condrestart(){ + [ -e /var/lock/subsys/auditd ] && restart + return 0 +} + + +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + reload|force-reload) + reload + ;; + rotate) + rotate + ;; + resume) + resume + ;; + condrestart|try-restart) + condrestart + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|rotate|resume}" + RETVAL=3 +esac + +exit $RETVAL + diff --git a/framework/src/audit/init.d/auditd.restart b/framework/src/audit/init.d/auditd.restart new file mode 100755 index 00000000..42669ff1 --- /dev/null +++ b/framework/src/audit/init.d/auditd.restart @@ -0,0 +1,13 @@ +#!/bin/sh +# Helper script to provide legacy auditd service options not +# directly supported by systemd. + +test -f /etc/audit/auditd.conf || exit 6 + +/usr/libexec/initscripts/legacy-actions/auditd/stop +sleep 1 +echo "Redirecting start to /bin/systemctl start auditd.service" +/bin/systemctl start auditd.service +RETVAL="$?" + +exit $RETVAL diff --git a/framework/src/audit/init.d/auditd.resume b/framework/src/audit/init.d/auditd.resume new file mode 100644 index 00000000..55c71a4b --- /dev/null +++ b/framework/src/audit/init.d/auditd.resume @@ -0,0 +1,16 @@ +#!/bin/sh +# Helper script to provide legacy auditd service options not +# directly supported by systemd + +# Check that we are root ... so non-root users stop here +test $EUID = 0 || exit 4 + +PATH=/sbin:/bin:/usr/bin:/usr/sbin +prog="auditd" +source /etc/init.d/functions + +echo -n $"Resuming logging: " +killproc $prog -USR2 +RETVAL=$? +echo +exit $RETVAL diff --git a/framework/src/audit/init.d/auditd.rotate b/framework/src/audit/init.d/auditd.rotate new file mode 100644 index 00000000..e89850a6 --- /dev/null +++ b/framework/src/audit/init.d/auditd.rotate @@ -0,0 +1,16 @@ +#!/bin/sh +# Helper script to provide legacy auditd service options not +# directly supported by systemd + +# Check that we are root ... so non-root users stop here +test $EUID = 0 || exit 4 + +PATH=/sbin:/bin:/usr/bin:/usr/sbin +prog="auditd" +source /etc/init.d/functions + +echo -n $"Rotating logs: " +killproc $prog -USR1 +RETVAL=$? +echo +exit $RETVAL diff --git a/framework/src/audit/init.d/auditd.service b/framework/src/audit/init.d/auditd.service new file mode 100644 index 00000000..5921c1cd --- /dev/null +++ b/framework/src/audit/init.d/auditd.service @@ -0,0 +1,22 @@ +[Unit] +Description=Security Auditing Service +DefaultDependencies=no +After=local-fs.target systemd-tmpfiles-setup.service +Conflicts=shutdown.target +Before=sysinit.target shutdown.target +RefuseManualStop=yes +ConditionKernelCommandLine=!audit=0 + +[Service] +ExecStart=/sbin/auditd -n +## To use augenrules, copy this file to /etc/systemd/system/auditd.service +## and uncomment the next line and delete/comment out the auditctl line. +## Then copy existing rules to /etc/audit/rules.d/ +## Not doing this last step can cause loss of existing rules +#ExecStartPost=-/sbin/augenrules --load +ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target + diff --git a/framework/src/audit/init.d/auditd.stop b/framework/src/audit/init.d/auditd.stop new file mode 100644 index 00000000..009da23c --- /dev/null +++ b/framework/src/audit/init.d/auditd.stop @@ -0,0 +1,16 @@ +#!/bin/sh +# Helper script to provide legacy auditd service options not +# directly supported by systemd + +# Check that we are root ... so non-root users stop here +test $EUID = 0 || exit 4 + +PATH=/sbin:/bin:/usr/bin:/usr/sbin +prog="auditd" +source /etc/init.d/functions + +echo -n $"Stopping logging: " +killproc $prog -TERM +RETVAL=$? +echo +exit $RETVAL diff --git a/framework/src/audit/init.d/auditd.sysconfig b/framework/src/audit/init.d/auditd.sysconfig new file mode 100644 index 00000000..1485539a --- /dev/null +++ b/framework/src/audit/init.d/auditd.sysconfig @@ -0,0 +1,24 @@ +# Add extra options here +EXTRAOPTIONS="" +# +# This is the locale information that audit uses. Its defaulted to en_US. +# To remove all locale information from audit's environment, set +# AUDITD_LANG to the empty string or the string "none". +AUDITD_LANG="en_US" +# +# This option is used to determine if rules & watches should be deleted on +# shutdown. This is beneficial in most cases so that a watch doesn't linger +# on a drive that is being unmounted. If set to no, it will NOT be cleaned up. +AUDITD_CLEAN_STOP="yes" +# +# This option determines whether the audit system should be disabled when +# the audit daemon is shutdown +AUDITD_STOP_DISABLE="yes" +# +# This option determines whether or not to call augenrules to compile the +# audit.rule file from /etc/audit/rules.d. The default is "no" so that nothing +# happens to existing rules. When setting this up, any existing rules need to +# be copied into /etc/audit/rules.d or it will be lost when audit.rule gets +# overwritten. +USE_AUGENRULES="no" + diff --git a/framework/src/audit/init.d/augenrules b/framework/src/audit/init.d/augenrules new file mode 100644 index 00000000..aa0758f6 --- /dev/null +++ b/framework/src/audit/init.d/augenrules @@ -0,0 +1,130 @@ +#!/bin/bash + +# Script to concatenate rules files found in a base audit rules directory +# to form a single /etc/audit/audit.rules file suitable for loading into +# the Linux audit system + +# When forming the interim rules file, both empty lines and comment +# lines (starting with # or #) are stripped as the source files +# are processed. +# +# Having formed the interim rules file, the script checks if the file is empty +# or is identical to the existing /etc/audit/audit.rules and if either of +# these cases are true, it does not replace the existing file +# + +# Variables +# +# DestinationFile: +# Destination rules file +# SourceRulesDir: +# Directory location to find component rule files +# TmpRules: +# Temporary interim rules file +# ASuffix: +# Suffix for previous audit.rules file if this script replaces it. +# The file is left in the destination directory with suffix with $ASuffix + +DestinationFile=/etc/audit/audit.rules +SourceRulesDir=/etc/audit/rules.d +TmpRules=`mktemp /tmp/aurules.XXXXXXXX` +ASuffix="prev" +OnlyCheck=0 +LoadRules=0 +RETVAL=0 +usage="Usage: $0 [--check|--load]" + +# Delete the interim file on faults +trap 'rm -f ${TmpRules}; exit 1' 1 2 3 13 15 + +try_load() { + if [ $LoadRules -eq 1 ] ; then + auditctl -R ${DestinationFile} + RETVAL=$? + fi +} + +while [ $# -ge 1 ] +do + if [ "$1" = "--check" ] ; then + OnlyCheck=1 + elif [ "$1" = "--load" ] ; then + LoadRules=1 + else + echo "$usage" + exit 1 + fi + shift +done + +# Check environment +if [ ! -d ${SourceRulesDir} ]; then + echo "$0: No rules directory - ${SourceRulesDir}" + rm -f ${TmpRules} + try_load + exit 1 +fi + +# Create the interim rules file ensuring its access modes protect it +# from normal users and strip empty lines and comment lines. We also ensure +# - the last processed -D directive without an option is emitted as the first +# line. -D directives with options are left in place +# - the last processed -b directory is emitted as the second line +# - the last processed -f directory is emitted as the third line +# - the last processed -e directive is emitted as the last line +umask 0137 +echo "## This file is automatically generated from $SourceRulesDir" >> ${TmpRules} +for rules in $(/bin/ls -1v ${SourceRulesDir} | grep ".rules$") ; do + cat ${SourceRulesDir}/${rules} +done | awk '\ +BEGIN { + minus_e = ""; + minus_D = ""; + minus_f = ""; + minus_b = ""; + rest = 0; +} { + if (length($0) < 1) { next; } + if (match($0, "^\\s*#")) { next; } + if (match($0, "^\\s*-e")) { minus_e = $0; next; } + if (match($0, "^\\s*-D\\s*$")) { minus_D = $0; next; } + if (match($0, "^\\s*-f")) { minus_f = $0; next; } + if (match($0, "^\\s*-b")) { minus_b = $0; next; } + rules[rest++] = $0; +} +END { + printf "%s\n%s\n%s\n", minus_D, minus_b, minus_f; + for (i = 0; i < rest; i++) { printf "%s\n", rules[i]; } + printf "%s\n", minus_e; +}' >> ${TmpRules} + +# If empty then quit +if [ ! -s ${TmpRules} ]; then + echo "$0: No rules" + rm -f ${TmpRules} + try_load + exit $RETVAL +fi + +# If the same then quit +cmp -s ${TmpRules} ${DestinationFile} > /dev/null 2>&1 +if [ $? -eq 0 ]; then + echo "$0: No change" + rm -f ${TmpRules} + try_load + exit $RETVAL +elif [ $OnlyCheck -eq 1 ] ; then + echo "$0: Rules have changed and should be updated" + exit 0 +fi + +# Otherwise we install the new file +if [ -f ${DestinationFile} ]; then + cp ${DestinationFile} ${DestinationFile}.prev +fi +# We copy the file so that it gets the right selinux lable +cp ${TmpRules} ${DestinationFile} +rm -f ${TmpRules} + +try_load +exit $RETVAL diff --git a/framework/src/audit/init.d/libaudit.conf b/framework/src/audit/init.d/libaudit.conf new file mode 100644 index 00000000..90855d72 --- /dev/null +++ b/framework/src/audit/init.d/libaudit.conf @@ -0,0 +1,7 @@ +# This is the configuration file for libaudit tunables. +# It is currently only used for the failure_action tunable. + +# failure_action can be: log, ignore, terminate +failure_action = ignore + + diff --git a/framework/src/audit/install-sh b/framework/src/audit/install-sh new file mode 100755 index 00000000..377bb868 --- /dev/null +++ b/framework/src/audit/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-11-20.07; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/framework/src/audit/lib/Makefile.am b/framework/src/audit/lib/Makefile.am new file mode 100644 index 00000000..e2ed1019 --- /dev/null +++ b/framework/src/audit/lib/Makefile.am @@ -0,0 +1,265 @@ +# Makefile.am -- +# Copyright 2004-2009,2013-15 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +SUBDIRS = test +CLEANFILES = $(BUILT_SOURCES) +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +EXTRA_DIST = syscall-update.txt +VERSION_INFO = 1:0 +AM_CFLAGS = -fPIC -DPIC -D_GNU_SOURCE +AM_CPPFLAGS = -I. -I${top_srcdir} -I${top_srcdir}/auparse + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = audit.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libaudit.la +include_HEADERS = libaudit.h +libaudit_la_SOURCES = libaudit.c message.c netlink.c \ + lookup_table.c audit_logging.c deprecated.c \ + strsplit.c dso.h private.h errormsg.h +libaudit_la_LIBADD = +libaudit_la_DEPENDENCIES = $(libaudit_la_SOURCES) ../config.h +libaudit_la_LDFLAGS = -Wl,-z,relro -version-info $(VERSION_INFO) +nodist_libaudit_la_SOURCES = $(BUILT_SOURCES) + +BUILT_SOURCES = actiontabs.h errtabs.h fieldtabs.h flagtabs.h \ + ftypetabs.h i386_tables.h ia64_tables.h machinetabs.h \ + msg_typetabs.h optabs.h ppc_tables.h s390_tables.h \ + s390x_tables.h x86_64_tables.h +if USE_ALPHA +BUILT_SOURCES += alpha_tables.h +endif +if USE_ARM +BUILT_SOURCES += arm_tables.h +endif +if USE_AARCH64 +BUILT_SOURCES += aarch64_tables.h +endif +noinst_PROGRAMS = gen_actiontabs_h gen_errtabs_h gen_fieldtabs_h \ + gen_flagtabs_h gen_ftypetabs_h gen_i386_tables_h \ + gen_ia64_tables_h gen_machinetabs_h gen_msg_typetabs_h \ + gen_optabs_h gen_ppc_tables_h gen_s390_tables_h \ + gen_s390x_tables_h gen_x86_64_tables_h +if USE_ALPHA +noinst_PROGRAMS += gen_alpha_tables_h +endif +if USE_ARM +noinst_PROGRAMS += gen_arm_tables_h +endif +if USE_AARCH64 +noinst_PROGRAMS += gen_aarch64_tables_h +endif +gen_actiontabs_h_SOURCES = gen_tables.c gen_tables.h actiontab.h +gen_actiontabs_h_CFLAGS = '-DTABLE_H="actiontab.h"' +$(gen_actiontabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_actiontabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_actiontabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_actiontabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_actiontabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_actiontabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +actiontabs.h: gen_actiontabs_h Makefile + ./gen_actiontabs_h --lowercase --i2s --s2i action > $@ + +if USE_ALPHA +gen_alpha_tables_h_SOURCES = gen_tables.c gen_tables.h alpha_table.h +gen_alpha_tables_h_CFLAGS = '-DTABLE_H="alpha_table.h"' +$(gen_alpha_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_alpha_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_alpha_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_alpha_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_alpha_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_alpha_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +alpha_tables.h: gen_alpha_tables_h Makefile + ./gen_alpha_tables_h --lowercase --i2s --s2i alpha_syscall > $@ +endif + +if USE_ARM +gen_arm_tables_h_SOURCES = gen_tables.c gen_tables.h arm_table.h +gen_arm_tables_h_CFLAGS = '-DTABLE_H="arm_table.h"' +$(gen_arm_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_arm_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_arm_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_arm_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_arm_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_arm_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +arm_tables.h: gen_arm_tables_h Makefile + ./gen_arm_tables_h --lowercase --i2s --s2i arm_syscall > $@ +endif + +if USE_AARCH64 +gen_aarch64_tables_h_SOURCES = gen_tables.c gen_tables.h aarch64_table.h +gen_aarch64_tables_h_CFLAGS = '-DTABLE_H="aarch64_table.h"' +$(gen_aarch64_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_aarch64_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_aarch64_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_aarch64_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_aarch64_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_aarch64_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +aarch64_tables.h: gen_aarch64_tables_h Makefile + ./gen_aarch64_tables_h --lowercase --i2s --s2i aarch64_syscall > $@ +endif + +gen_errtabs_h_SOURCES = gen_tables.c gen_tables.h errtab.h +gen_errtabs_h_CFLAGS = '-DTABLE_H="errtab.h"' +$(gen_errtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_errtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_errtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_errtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_errtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_errtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +errtabs.h: gen_errtabs_h Makefile + ./gen_errtabs_h --duplicate-ints --uppercase --i2s --s2i err > $@ + +gen_fieldtabs_h_SOURCES = gen_tables.c gen_tables.h fieldtab.h +gen_fieldtabs_h_CFLAGS = '-DTABLE_H="fieldtab.h"' +$(gen_fieldtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_fieldtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_fieldtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_fieldtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_fieldtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_fieldtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +fieldtabs.h: gen_fieldtabs_h Makefile + ./gen_fieldtabs_h --duplicate-ints --lowercase --i2s --s2i field > $@ + +gen_flagtabs_h_SOURCES = gen_tables.c gen_tables.h flagtab.h +gen_flagtabs_h_CFLAGS = '-DTABLE_H="flagtab.h"' +$(gen_flagtabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_flagtabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_flagtabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_flagtabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_flagtabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_flagtabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +flagtabs.h: gen_flagtabs_h Makefile + ./gen_flagtabs_h --lowercase --i2s --s2i flag > $@ + +gen_ftypetabs_h_SOURCES = gen_tables.c gen_tables.h ftypetab.h +gen_ftypetabs_h_CFLAGS = '-DTABLE_H="ftypetab.h"' +$(gen_ftypetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ftypetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ftypetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ftypetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ftypetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ftypetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ftypetabs.h: gen_ftypetabs_h Makefile + ./gen_ftypetabs_h --lowercase --i2s --s2i ftype > $@ + +gen_i386_tables_h_SOURCES = gen_tables.c gen_tables.h i386_table.h +gen_i386_tables_h_CFLAGS = '-DTABLE_H="i386_table.h"' +$(gen_i386_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_i386_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_i386_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_i386_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_i386_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_i386_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +i386_tables.h: gen_i386_tables_h Makefile + ./gen_i386_tables_h --duplicate-ints --lowercase --i2s --s2i \ + i386_syscall > $@ + +gen_ia64_tables_h_SOURCES = gen_tables.c gen_tables.h ia64_table.h +gen_ia64_tables_h_CFLAGS = '-DTABLE_H="ia64_table.h"' +$(gen_ia64_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ia64_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ia64_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ia64_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ia64_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ia64_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ia64_tables.h: gen_ia64_tables_h Makefile + ./gen_ia64_tables_h --lowercase --i2s --s2i ia64_syscall > $@ + +gen_machinetabs_h_SOURCES = gen_tables.c gen_tables.h machinetab.h +gen_machinetabs_h_CFLAGS = '-DTABLE_H="machinetab.h"' +$(gen_machinetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_machinetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_machinetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_machinetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_machinetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_machinetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +machinetabs.h: gen_machinetabs_h Makefile + ./gen_machinetabs_h --duplicate-ints --lowercase --i2s --s2i machine \ + > $@ + +gen_msg_typetabs_h_SOURCES = gen_tables.c gen_tables.h msg_typetab.h +gen_msg_typetabs_h_CFLAGS = '-DTABLE_H="msg_typetab.h"' +$(gen_msg_typetabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_msg_typetabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_msg_typetabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_msg_typetabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_msg_typetabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_msg_typetabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +msg_typetabs.h: gen_msg_typetabs_h Makefile + ./gen_msg_typetabs_h --uppercase --i2s --s2i msg_type > $@ + +gen_optabs_h_SOURCES = gen_tables.c gen_tables.h optab.h +gen_optabs_h_CFLAGS = '-DTABLE_H="optab.h"' +$(gen_optabs_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_optabs_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_optabs_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_optabs_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_optabs_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_optabs_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +optabs.h: gen_optabs_h Makefile + ./gen_optabs_h --i2s op > $@ + +gen_ppc_tables_h_SOURCES = gen_tables.c gen_tables.h ppc_table.h +gen_ppc_tables_h_CFLAGS = '-DTABLE_H="ppc_table.h"' +$(gen_ppc_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_ppc_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_ppc_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_ppc_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_ppc_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_ppc_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +ppc_tables.h: gen_ppc_tables_h Makefile + ./gen_ppc_tables_h --lowercase --i2s --s2i ppc_syscall > $@ + +gen_s390_tables_h_SOURCES = gen_tables.c gen_tables.h s390_table.h +gen_s390_tables_h_CFLAGS = '-DTABLE_H="s390_table.h"' +$(gen_s390_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_s390_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_s390_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_s390_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_s390_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_s390_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +s390_tables.h: gen_s390_tables_h Makefile + ./gen_s390_tables_h --lowercase --i2s --s2i s390_syscall > $@ + +gen_s390x_tables_h_SOURCES = gen_tables.c gen_tables.h s390x_table.h +gen_s390x_tables_h_CFLAGS = '-DTABLE_H="s390x_table.h"' +$(gen_s390x_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_s390x_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_s390x_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_s390x_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_s390x_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_s390x_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +s390x_tables.h: gen_s390x_tables_h Makefile + ./gen_s390x_tables_h --lowercase --i2s --s2i s390x_syscall > $@ + +gen_x86_64_tables_h_SOURCES = gen_tables.c gen_tables.h x86_64_table.h +gen_x86_64_tables_h_CFLAGS = '-DTABLE_H="x86_64_table.h"' +$(gen_x86_64_tables_h_OBJECTS): CC=$(CC_FOR_BUILD) +$(gen_x86_64_tables_h_OBJECTS): CFLAGS=$(CFLAGS_FOR_BUILD) +$(gen_x86_64_tables_h_OBJECTS): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +gen_x86_64_tables_h$(BUILD_EXEEXT): CC=$(CC_FOR_BUILD) +gen_x86_64_tables_h$(BUILD_EXEEXT): CFLAGS=$(CFLAGS_FOR_BUILD) +gen_x86_64_tables_h$(BUILD_EXEEXT): CPPFLAGS=$(CPPFLAGS_FOR_BUILD) +x86_64_tables.h: gen_x86_64_tables_h Makefile + ./gen_x86_64_tables_h --lowercase --i2s --s2i x86_64_syscall > $@ diff --git a/framework/src/audit/lib/aarch64_table.h b/framework/src/audit/lib/aarch64_table.h new file mode 100644 index 00000000..bf64b98a --- /dev/null +++ b/framework/src/audit/lib/aarch64_table.h @@ -0,0 +1,288 @@ +/* aarch64_table.h -- + * Copyright 2013-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(0, "io_setup") +_S(1, "io_destroy") +_S(2, "io_submit") +_S(3, "io_cancel") +_S(4, "io_getevents") +_S(5, "setxattr") +_S(6, "lsetxattr") +_S(7, "fsetxattr") +_S(8, "getxattr") +_S(9, "lgetxattr") +_S(10, "fgetxattr") +_S(11, "listxattr") +_S(12, "llistxattr") +_S(13, "flistxattr") +_S(14, "removexattr") +_S(15, "lremovexattr") +_S(16, "fremovexattr") +_S(17, "getcwd") +_S(18, "lookup_dcookie") +_S(19, "eventfd2") +_S(20, "epoll_create1") +_S(21, "epoll_ctl") +_S(22, "epoll_pwait") +_S(23, "dup") +_S(24, "dup3") +_S(25, "fcntl") +_S(26, "inotify_init1") +_S(27, "inotify_add_watch") +_S(28, "inotify_rm_watch") +_S(29, "ioctl") +_S(30, "ioprio_set") +_S(31, "ioprio_get") +_S(32, "flock") +_S(33, "mknodat") +_S(34, "mkdirat") +_S(35, "unlinkat") +_S(36, "symlinkat") +_S(37, "linkat") +_S(38, "renameat") +_S(39, "umount2") +_S(40, "mount") +_S(41, "pivot_root") +_S(42, "nfsservctl") +_S(43, "statfs") +_S(44, "fstatfs") +_S(45, "truncate") +_S(46, "ftruncate") +_S(47, "fallocate") +_S(48, "faccessat") +_S(49, "chdir") +_S(50, "fchdir") +_S(51, "chroot") +_S(52, "fchmod") +_S(53, "fchmodat") +_S(54, "fchownat") +_S(55, "fchown") +_S(56, "openat") +_S(57, "close") +_S(58, "vhangup") +_S(59, "pipe2") +_S(60, "quotactl") +_S(61, "getdents") +_S(62, "lseek") +_S(63, "read") +_S(64, "write") +_S(65, "readv") +_S(66, "writev") +_S(67, "pread") +_S(68, "pwrite") +_S(69, "preadv") +_S(70, "pwritev") +_S(71, "sendfile") +_S(72, "pselect6") +_S(73, "ppoll") +_S(74, "signalfd4") +_S(75, "vmsplice") +_S(76, "splice") +_S(77, "tee") +_S(78, "readlinkat") +_S(79, "newfstatat") +_S(80, "newfstat") +_S(81, "sync") +_S(82, "fsync") +_S(83, "fdatasync") +_S(84, "sync_file_range") +_S(85, "timerfd_create") +_S(86, "timerfd_settime") +_S(87, "timerfd_gettime") +_S(88, "utimensat") +_S(89, "acct") +_S(90, "capget") +_S(91, "capset") +_S(92, "personality") +_S(93, "exit") +_S(94, "exit_group") +_S(95, "waitid") +_S(96, "set_tid_address") +_S(97, "unshare") +_S(98, "futex") +_S(99, "set_robust_list") +_S(100, "get_robust_list") +_S(101, "nanosleep") +_S(102, "getitimer") +_S(103, "setitimer") +_S(104, "kexec_load") +_S(105, "init_module") +_S(106, "delete_module") +_S(107, "timer_create") +_S(108, "timer_gettime") +_S(109, "timer_getoverrun") +_S(110, "timer_settime") +_S(111, "timer_delete") +_S(112, "clock_settime") +_S(113, "clock_gettime") +_S(114, "clock_getres") +_S(115, "clock_nanosleep") +_S(116, "syslog") +_S(117, "ptrace") +_S(118, "sched_setparam") +_S(119, "sched_setscheduler") +_S(120, "sched_getscheduler") +_S(121, "sched_getparam") +_S(122, "sched_setaffinity") +_S(123, "sched_getaffinity") +_S(124, "sched_yield") +_S(125, "sched_get_priority_max") +_S(126, "sched_get_priority_min") +_S(127, "sched_rr_get_interval") +_S(128, "restart_syscall") +_S(129, "kill") +_S(130, "tkill") +_S(131, "tgkill") +_S(132, "sigaltstack") +_S(133, "rt_sigsuspend") +_S(134, "rt_sigaction") +_S(135, "rt_sigprocmask") +_S(136, "rt_sigpending") +_S(137, "rt_sigtimedwait") +_S(138, "rt_sigqueueinfo") +_S(139, "rt_sigreturn") +_S(140, "setpriority") +_S(141, "getpriority") +_S(142, "reboot") +_S(143, "setregid") +_S(144, "setgid") +_S(145, "setreuid") +_S(146, "setuid") +_S(147, "setresuid") +_S(148, "getresuid") +_S(149, "setresgid") +_S(150, "getresgid") +_S(151, "setfsuid") +_S(152, "setfsgid") +_S(153, "times") +_S(154, "setpgid") +_S(155, "getpgid") +_S(156, "getsid") +_S(157, "setsid") +_S(158, "getgroups") +_S(159, "setgroups") +_S(160, "uname") +_S(161, "sethostname") +_S(162, "setdomainname") +_S(163, "getrlimit") +_S(164, "setrlimit") +_S(165, "getrusage") +_S(166, "umask") +_S(167, "prctl") +_S(168, "getcpu") +_S(169, "gettimeofday") +_S(170, "settimeofday") +_S(171, "adjtimex") +_S(172, "getpid") +_S(173, "getppid") +_S(174, "getuid") +_S(175, "geteuid") +_S(176, "getgid") +_S(177, "getegid") +_S(178, "gettid") +_S(179, "sysinfo") +_S(180, "mq_open") +_S(181, "mq_unlink") +_S(182, "mq_timedsend") +_S(183, "mq_timedreceive") +_S(184, "mq_notify") +_S(185, "mq_getsetattr") +_S(186, "msgget") +_S(187, "msgctl") +_S(188, "msgrcv") +_S(189, "msgsnd") +_S(190, "semget") +_S(191, "semctl") +_S(192, "semtimedop") +_S(193, "semop") +_S(194, "shmget") +_S(195, "shmctl") +_S(196, "shmat") +_S(197, "shmdt") +_S(198, "socket") +_S(199, "socketpair") +_S(200, "bind") +_S(201, "listen") +_S(202, "accept") +_S(203, "connect") +_S(204, "getsockname") +_S(205, "getpeername") +_S(206, "sendto") +_S(207, "recvfrom") +_S(208, "setsockopt") +_S(209, "getsockopt") +_S(210, "shutdown") +_S(211, "sendmsg") +_S(212, "recvmsg") +_S(213, "readahead") +_S(214, "brk") +_S(215, "munmap") +_S(216, "mremap") +_S(217, "add_key") +_S(218, "request_key") +_S(219, "keyctl") +_S(220, "clone") +_S(221, "execve") +_S(222, "mmap") +_S(223, "fadvise64") +_S(224, "swapon") +_S(225, "swapoff") +_S(226, "mprotect") +_S(227, "msync") +_S(228, "mlock") +_S(229, "munlock") +_S(230, "mlockall") +_S(231, "munlockall") +_S(232, "mincore") +_S(233, "madvise") +_S(234, "remap_file_pages") +_S(235, "mbind") +_S(236, "get_mempolicy") +_S(237, "set_mempolicy") +_S(238, "migrate_pages") +_S(239, "move_pages") +_S(240, "rt_tgsigqueueinfo") +_S(241, "perf_event_open") +_S(242, "accept4") +_S(243, "recvmmsg") +_S(260, "wait4") +_S(261, "prlimit64") +_S(262, "fanotify_init") +_S(263, "fanotify_mark") +_S(264, "name_to_handle_at") +_S(265, "open_by_handle_at") +_S(266, "clock_adjtime") +_S(267, "syncfs") +_S(268, "setns") +_S(269, "sendmmsg") +_S(270, "process_vm_readv") +_S(271, "process_vm_writev") +_S(272, "kcmp") +_S(273, "finit_module") +_S(274, "sched_setattr") +_S(275, "sched_getattr") +_S(276, "renameat2") +_S(277, "seccomp") +_S(278, "getrandom") +_S(279, "memfd_create") +_S(280, "bpf") +_S(281, "execveat") diff --git a/framework/src/audit/lib/actiontab.h b/framework/src/audit/lib/actiontab.h new file mode 100644 index 00000000..12744229 --- /dev/null +++ b/framework/src/audit/lib/actiontab.h @@ -0,0 +1,25 @@ +/* actiontab.h -- + * Copyright 2005,2006 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(AUDIT_NEVER, "never" ) +_S(AUDIT_POSSIBLE, "possible" ) +_S(AUDIT_ALWAYS, "always" ) diff --git a/framework/src/audit/lib/alpha_table.h b/framework/src/audit/lib/alpha_table.h new file mode 100644 index 00000000..c798f834 --- /dev/null +++ b/framework/src/audit/lib/alpha_table.h @@ -0,0 +1,453 @@ +/* alpha_table.h -- + * Copyright 2005-07,2010-12,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(0, "osf_syscall") +_S(1, "exit") +_S(2, "fork") +_S(3, "read") +_S(4, "write") +_S(5, "osf_old_open") +_S(6, "close") +_S(7, "osf_wait4") +_S(8, "osf_old_creat") +_S(9, "link") +_S(10, "unlink") +_S(11, "osf_execve") +_S(12, "chdir") +_S(13, "fchdir") +_S(14, "mknod") +_S(15, "chmod") +_S(16, "chown") +_S(17, "brk") +_S(18, "osf_getfsstat") +_S(19, "lseek") +_S(20, "getxpid") +_S(21, "osf_mount") +_S(22, "umount") +_S(23, "setuid") +_S(24, "getxuid") +_S(25, "exec_with_loader") +_S(26, "ptrace") +_S(27, "osf_nrecvmsg") +_S(28, "osf_nsendmsg") +_S(29, "osf_nrecvfrom") +_S(30, "osf_naccept") +_S(31, "osf_ngetpeername") +_S(32, "osf_ngetsockname") +_S(33, "access") +_S(34, "osf_chflags") +_S(35, "osf_fchflags") +_S(36, "sync") +_S(37, "kill") +_S(38, "osf_old_stat") +_S(39, "setpgid") +_S(40, "osf_old_lstat") +_S(41, "dup") +_S(42, "pipe") +_S(43, "osf_set_program_attributes") +_S(44, "osf_profil") +_S(45, "open") +_S(46, "osf_old_sigaction") +_S(47, "getxgid") +_S(48, "osf_sigprocmask") +_S(49, "osf_getlogin") +_S(50, "osf_setlogin") +_S(51, "acct") +_S(52, "sigpending") + +_S(54, "ioctl") +_S(55, "osf_reboot") +_S(56, "osf_revoke") +_S(57, "symlink") +_S(58, "readlink") +_S(59, "execve") +_S(60, "umask") +_S(61, "chroot") +_S(62, "osf_old_fstat") +_S(63, "getpgrp") +_S(64, "getpagesize") +_S(65, "osf_mremap") +_S(66, "vfork") +_S(67, "stat") +_S(68, "lstat") +_S(69, "osf_sbrk") +_S(70, "osf_sstk") +_S(71, "mmap") +_S(72, "osf_old_vadvise") +_S(73, "munmap") +_S(74, "mprotect") +_S(75, "madvise") +_S(76, "vhangup") +_S(77, "osf_kmodcall") +_S(78, "osf_mincore") +_S(79, "getgroups") +_S(80, "setgroups") +_S(81, "osf_old_getpgrp") +_S(82, "setpgrp") +_S(83, "osf_setitimer") +_S(84, "osf_old_wait") +_S(85, "osf_table") +_S(86, "osf_getitimer") +_S(87, "gethostname") +_S(88, "sethostname") +_S(89, "getdtablesize") +_S(90, "dup2") +_S(91, "fstat") +_S(92, "fcntl") +_S(93, "osf_select") +_S(94, "poll") +_S(95, "fsync") +_S(96, "setpriority") +_S(97, "socket") +_S(98, "connect") +_S(99, "accept") +_S(100, "getpriority") +_S(101, "send") +_S(102, "recv") +_S(103, "sigreturn") +_S(104, "bind") +_S(105, "setsockopt") +_S(106, "listen") +_S(107, "osf_plock") +_S(108, "osf_old_sigvec") +_S(109, "osf_old_sigblock") +_S(110, "osf_old_sigsetmask") +_S(111, "sigsuspend") +_S(112, "osf_sigstack") +_S(113, "recvmsg") +_S(114, "sendmsg") +_S(115, "osf_old_vtrace") +_S(116, "osf_gettimeofday") +_S(117, "osf_getrusage") +_S(118, "getsockopt") + +_S(120, "readv") +_S(121, "writev") +_S(122, "osf_settimeofday") +_S(123, "fchown") +_S(124, "fchmod") +_S(125, "recvfrom") +_S(126, "setreuid") +_S(127, "setregid") +_S(128, "rename") +_S(129, "truncate") +_S(130, "ftruncate") +_S(131, "flock") +_S(132, "setgid") +_S(133, "sendto") +_S(134, "shutdown") +_S(135, "socketpair") +_S(136, "mkdir") +_S(137, "rmdir") +_S(138, "osf_utimes") +_S(139, "osf_old_sigreturn") +_S(140, "osf_adjtime") +_S(141, "getpeername") +_S(142, "osf_gethostid") +_S(143, "osf_sethostid") +_S(144, "getrlimit") +_S(145, "setrlimit") +_S(146, "osf_old_killpg") +_S(147, "setsid") +_S(148, "quotactl") +_S(149, "osf_oldquota") +_S(150, "getsockname") + +_S(153, "osf_pid_block") +_S(154, "osf_pid_unblock") + +_S(156, "sigaction") +_S(157, "osf_sigwaitprim") +_S(158, "osf_nfssvc") +_S(159, "osf_getdirentries") +_S(160, "osf_statfs") +_S(161, "osf_fstatfs") + +_S(163, "osf_asynch_daemon") +_S(164, "osf_getfh") +_S(165, "osf_getdomainname") +_S(166, "setdomainname") + +_S(169, "osf_exportfs") + +_S(181, "osf_alt_plock") + +_S(184, "osf_getmnt") + +_S(187, "osf_alt_sigpending") +_S(188, "osf_alt_setsid") + +_S(199, "osf_swapon") +_S(200, "msgctl") +_S(201, "msgget") +_S(202, "msgrcv") +_S(203, "msgsnd") +_S(204, "semctl") +_S(205, "semget") +_S(206, "semop") +_S(207, "osf_utsname") +_S(208, "lchown") +_S(209, "osf_shmat") +_S(210, "shmctl") +_S(211, "shmdt") +_S(212, "shmget") +_S(213, "osf_mvalid") +_S(214, "osf_getaddressconf") +_S(215, "osf_msleep") +_S(216, "osf_mwakeup") +_S(217, "msync") +_S(218, "osf_signal") +_S(219, "osf_utc_gettime") +_S(220, "osf_utc_adjtime") + +_S(222, "osf_security") +_S(223, "osf_kloadcall") + +_S(233, "getpgid") +_S(234, "getsid") +_S(235, "sigaltstack") +_S(236, "osf_waitid") +_S(237, "osf_priocntlset") +_S(238, "osf_sigsendset") +_S(239, "osf_set_speculative") +_S(240, "osf_msfs_syscall") +_S(241, "osf_sysinfo") +_S(242, "osf_uadmin") +_S(243, "osf_fuser") +_S(244, "osf_proplist_syscall") +_S(245, "osf_ntp_adjtime") +_S(246, "osf_ntp_gettime") +_S(247, "osf_pathconf") +_S(248, "osf_fpathconf") + +_S(250, "osf_uswitch") +_S(251, "osf_usleep_thread") +_S(252, "osf_audcntl") +_S(253, "osf_audgen") +_S(254, "sysfs") +_S(255, "osf_subsys_info") +_S(256, "osf_getsysinfo") +_S(257, "osf_setsysinfo") +_S(258, "osf_afs_syscall") +_S(259, "osf_swapctl") +_S(260, "osf_memcntl") +_S(261, "osf_fdatasync") + +_S(300, "bdflush") +_S(301, "sethae") +_S(302, "mount") +_S(303, "old_adjtimex") +_S(304, "swapoff") +_S(305, "getdents") +_S(306, "create_module") +_S(307, "init_module") +_S(308, "delete_module") +_S(309, "get_kernel_syms") +_S(310, "syslog") +_S(311, "reboot") +_S(312, "clone") +_S(313, "uselib") +_S(314, "mlock") +_S(315, "munlock") +_S(316, "mlockall") +_S(317, "munlockall") +_S(318, "sysinfo") +_S(319, "_sysctl") +/* 320 was sys_idle. */ +_S(321, "oldumount") +_S(322, "swapon") +_S(323, "times") +_S(324, "personality") +_S(325, "setfsuid") +_S(326, "setfsgid") +_S(327, "ustat") +_S(328, "statfs") +_S(329, "fstatfs") +_S(330, "sched_setparam") +_S(331, "sched_getparam") +_S(332, "sched_setscheduler") +_S(333, "sched_getscheduler") +_S(334, "sched_yield") +_S(335, "sched_get_priority_max") +_S(336, "sched_get_priority_min") +_S(337, "sched_rr_get_interval") +_S(338, "afs_syscall") +_S(339, "uname") +_S(340, "nanosleep") +_S(341, "mremap") +_S(342, "nfsservctl") +_S(343, "setresuid") +_S(344, "getresuid") +_S(345, "pciconfig_read") +_S(346, "pciconfig_write") +_S(347, "query_module") +_S(348, "prctl") +_S(349, "pread") +_S(350, "pwrite") +_S(351, "rt_sigreturn") +_S(352, "rt_sigaction") +_S(353, "rt_sigprocmask") +_S(354, "rt_sigpending") +_S(355, "rt_sigtimedwait") +_S(356, "rt_sigqueueinfo") +_S(357, "rt_sigsuspend") +_S(358, "select") +_S(359, "gettimeofday") +_S(360, "settimeofday") +_S(361, "getitimer") +_S(362, "setitimer") +_S(363, "utimes") +_S(364, "getrusage") +_S(365, "wait4") +_S(366, "adjtimex") +_S(367, "getcwd") +_S(368, "capget") +_S(369, "capset") +_S(370, "sendfile") +_S(371, "setresgid") +_S(372, "getresgid") +_S(373, "dipc") +_S(374, "pivot_root") +_S(375, "mincore") +_S(376, "pciconfig_iobase") +_S(377, "getdents64") +_S(378, "gettid") +_S(379, "readahead") +/* 380 is unused */ +_S(381, "tkill") +_S(382, "setxattr") +_S(383, "lsetxattr") +_S(384, "fsetxattr") +_S(385, "getxattr") +_S(386, "lgetxattr") +_S(387, "fgetxattr") +_S(388, "listxattr") +_S(389, "llistxattr") +_S(390, "flistxattr") +_S(391, "removexattr") +_S(392, "lremovexattr") +_S(393, "fremovexattr") +_S(394, "futex") +_S(395, "sched_setaffinity") +_S(396, "sched_getaffinity") +_S(397, "tuxcall") +_S(398, "io_setup") +_S(399, "io_destroy") +_S(400, "io_getevents") +_S(401, "io_submit") +_S(402, "io_cancel") +_S(405, "exit_group") +_S(406, "lookup_dcookie") +_S(407, "epoll_create") +_S(408, "epoll_ctl") +_S(409, "epoll_wait") +_S(410, "remap_file_pages") +_S(411, "set_tid_address") +_S(412, "restart_syscall") +_S(413, "fadvise64") +_S(424, "tgkill") +_S(425, "stat64") +_S(426, "lstat64") +_S(427, "fstat64") +_S(428, "vserver") +_S(429, "mbind") +_S(430, "get_mempolicy") +_S(431, "set_mempolicy") +_S(432, "mq_open") +_S(433, "mq_unlink") +_S(434, "mq_timedsend") +_S(435, "mq_timedreceive") +_S(436, "mq_notify") +_S(437, "mq_getsetattr") +_S(438, "waitid") +_S(439, "add_key") +_S(440, "request_key") +_S(441, "keyctl") +_S(442, "ioprio_set") +_S(443, "ioprio_get") +_S(444, "inotify_init") +_S(445, "inotify_add_watch") +_S(446, "inotify_rm_watch") +_S(447, "fdatasync") +_S(448, "kexec_load") +_S(449, "migrate_pages") +_S(450, "openat") +_S(451, "mkdirat") +_S(452, "mknodat") +_S(453, "fchownat") +_S(454, "futimesat") +_S(455, "fstatat64") +_S(456, "unlinkat") +_S(457, "renameat") +_S(458, "linkat") +_S(459, "symlinkat") +_S(460, "readlinkat") +_S(461, "fchmodat") +_S(462, "faccessat") +_S(463, "pselect6") +_S(464, "ppoll") +_S(465, "unshare") +_S(466, "set_robust_list") +_S(467, "get_robust_list") +_S(468, "splice") +_S(469, "sync_file_range") +_S(470, "tee") +_S(471, "vmsplice") +_S(472, "move_pages") +_S(473, "getcpu") +_S(474, "epoll_pwait") +_S(475, "utimensat") +_S(476, "signalfd") +_S(477, "timerfd") +_S(478, "eventfd") +_S(479, "recvmmsg") +_S(480, "fallocate") +_S(481, "timerfd_create") +_S(482, "timerfd_settime") +_S(483, "timerfd_gettime") +_S(484, "signalfd4") +_S(485, "eventfd2") +_S(486, "epoll_create1") +_S(487, "dup3") +_S(488, "pipe2") +_S(489, "inotify_init1") +_S(490, "preadv") +_S(491, "pwritev") +_S(492, "rt_tgsigqueueinfo") +_S(493, "perf_event_open") +_S(494, "fanotify_init") +_S(495, "fanotify_mark") +_S(496, "prlimit64") +_S(497, "name_to_handle_at") +_S(498, "open_by_handle_at") +_S(499, "clock_adjtime") +_S(500, "syncfs") +_S(501, "setns") +_S(502, "accept4") +_S(503, "sendmmsg") +_S(504, "process_vm_readv") +_S(505, "process_vm_writev") +_S(506, "kcmp") +_S(507, "finit_module") +_S(508, "sched_setattr") +_S(509, "sched_getattr") +_S(510, "renameat2") diff --git a/framework/src/audit/lib/arm_table.h b/framework/src/audit/lib/arm_table.h new file mode 100644 index 00000000..56a1c9fc --- /dev/null +++ b/framework/src/audit/lib/arm_table.h @@ -0,0 +1,373 @@ +/* arm_table.h -- + * Copyright 2009-10,2013-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +_S(0, "restart_syscall") +_S(1, "exit") +_S(2, "fork") +_S(3, "read") +_S(4, "write") +_S(5, "open") +_S(6, "close") +_S(8, "creat") +_S(9, "link") +_S(10, "unlink") +_S(11, "execve") +_S(12, "chdir") +_S(13, "time") +_S(14, "mknod") +_S(15, "chmod") +_S(16, "lchown") +_S(19, "lseek") +_S(20, "getpid") +_S(21, "mount") +_S(22, "umount") +_S(23, "setuid") +_S(24, "getuid") +_S(25, "stime") +_S(26, "ptrace") +_S(27, "alarm") +_S(29, "pause") +_S(30, "utime") +_S(33, "access") +_S(34, "nice") +_S(36, "sync") +_S(37, "kill") +_S(38, "rename") +_S(39, "mkdir") +_S(40, "rmdir") +_S(41, "dup") +_S(42, "pipe") +_S(43, "times") +_S(45, "brk") +_S(46, "setgid") +_S(47, "getgid") +_S(49, "geteuid") +_S(50, "getegid") +_S(51, "acct") +_S(52, "umount2") +_S(54, "ioctl") +_S(55, "fcntl") +_S(57, "setpgid") +_S(60, "umask") +_S(61, "chroot") +_S(62, "ustat") +_S(63, "dup2") +_S(64, "getppid") +_S(65, "getpgrp") +_S(66, "setsid") +_S(67, "sigaction") +_S(70, "setreuid") +_S(71, "setregid") +_S(72, "sigsuspend") +_S(73, "sigpending") +_S(74, "sethostname") +_S(75, "setrlimit") +_S(76, "getrlimit") +_S(77, "getrusage") +_S(78, "gettimeofday") +_S(79, "settimeofday") +_S(80, "getgroups") +_S(81, "setgroups") +_S(82, "select") +_S(83, "symlink") +_S(85, "readlink") +_S(86, "uselib") +_S(87, "swapon") +_S(88, "reboot") +_S(89, "readdir") +_S(90, "mmap") +_S(91, "munmap") +_S(92, "truncate") +_S(93, "ftruncate") +_S(94, "fchmod") +_S(95, "fchown") +_S(96, "getpriority") +_S(97, "setpriority") +_S(99, "statfs") +_S(100, "fstatfs") +_S(102, "socketcall") +_S(103, "syslog") +_S(104, "setitimer") +_S(105, "getitimer") +_S(106, "stat") +_S(107, "lstat") +_S(108, "fstat") +_S(111, "vhangup") +_S(113, "syscall") +_S(114, "wait4") +_S(115, "swapoff") +_S(116, "sysinfo") +_S(117, "ipc") +_S(118, "fsync") +_S(119, "sigreturn") +_S(120, "clone") +_S(121, "setdomainname") +_S(122, "uname") +_S(124, "adjtimex") +_S(125, "mprotect") +_S(126, "sigprocmask") +_S(128, "init_module") +_S(129, "delete_module") +_S(131, "quotactl") +_S(132, "getpgid") +_S(133, "fchdir") +_S(134, "bdflush") +_S(135, "sysfs") +_S(136, "personality") +_S(138, "setfsuid") +_S(139, "setfsgid") +_S(140, "llseek") +_S(141, "getdents") +_S(142, "newselect") +_S(143, "flock") +_S(144, "msync") +_S(145, "readv") +_S(146, "writev") +_S(147, "getsid") +_S(148, "fdatasync") +_S(149, "sysctl") +_S(150, "mlock") +_S(151, "munlock") +_S(152, "mlockall") +_S(153, "munlockall") +_S(154, "sched_setparam") +_S(155, "sched_getparam") +_S(156, "sched_setscheduler") +_S(157, "sched_getscheduler") +_S(158, "sched_yield") +_S(159, "sched_get_priority_max") +_S(160, "sched_get_priority_min") +_S(161, "sched_rr_get_interval") +_S(162, "nanosleep") +_S(163, "mremap") +_S(164, "setresuid") +_S(165, "getresuid") +_S(168, "poll") +_S(169, "nfsservctl") +_S(170, "setresgid") +_S(171, "getresgid") +_S(172, "prctl") +_S(173, "rt_sigreturn") +_S(174, "rt_sigaction") +_S(175, "rt_sigprocmask") +_S(176, "rt_sigpending") +_S(177, "rt_sigtimedwait") +_S(178, "rt_sigqueueinfo") +_S(179, "rt_sigsuspend") +_S(180, "pread64") +_S(181, "pwrite64") +_S(182, "chown") +_S(183, "getcwd") +_S(184, "capget") +_S(185, "capset") +_S(186, "sigaltstack") +_S(187, "sendfile") +_S(190, "vfork") +_S(191, "ugetrlimit") +_S(192, "mmap2") +_S(193, "truncate64") +_S(194, "ftruncate64") +_S(195, "stat64") +_S(196, "lstat64") +_S(197, "fstat64") +_S(198, "lchown32") +_S(199, "getuid32") +_S(200, "getgid32") +_S(201, "geteuid32") +_S(202, "getegid32") +_S(203, "setreuid32") +_S(204, "setregid32") +_S(205, "getgroups32") +_S(206, "setgroups32") +_S(207, "fchown32") +_S(208, "setresuid32") +_S(209, "getresuid32") +_S(210, "setresgid32") +_S(211, "getresgid32") +_S(212, "chown32") +_S(213, "setuid32") +_S(214, "setgid32") +_S(215, "setfsuid32") +_S(216, "setfsgid32") +_S(217, "getdents64") +_S(218, "pivot_root") +_S(219, "mincore") +_S(220, "madvise") +_S(221, "fcntl64") +_S(224, "gettid") +_S(225, "readahead") +_S(226, "setxattr") +_S(227, "lsetxattr") +_S(228, "fsetxattr") +_S(229, "getxattr") +_S(230, "lgetxattr") +_S(231, "fgetxattr") +_S(232, "listxattr") +_S(233, "llistxattr") +_S(234, "flistxattr") +_S(235, "removexattr") +_S(236, "lremovexattr") +_S(237, "fremovexattr") +_S(238, "tkill") +_S(239, "sendfile64") +_S(240, "futex") +_S(241, "sched_setaffinity") +_S(242, "sched_getaffinity") +_S(243, "io_setup") +_S(244, "io_destroy") +_S(245, "io_getevents") +_S(246, "io_submit") +_S(247, "io_cancel") +_S(248, "exit_group") +_S(249, "lookup_dcookie") +_S(250, "epoll_create") +_S(251, "epoll_ctl") +_S(252, "epoll_wait") +_S(253, "remap_file_pages") +_S(256, "set_tid_address") +_S(257, "timer_create") +_S(258, "timer_settime") +_S(259, "timer_gettime") +_S(260, "timer_getoverrun") +_S(261, "timer_delete") +_S(262, "clock_settime") +_S(263, "clock_gettime") +_S(264, "clock_getres") +_S(265, "clock_nanosleep") +_S(266, "statfs64") +_S(267, "fstatfs64") +_S(268, "tgkill") +_S(269, "utimes") +_S(270, "fadvise64_64") +_S(271, "pciconfig_iobase") +_S(272, "pciconfig_read") +_S(273, "pciconfig_write") +_S(274, "mq_open") +_S(275, "mq_unlink") +_S(276, "mq_timedsend") +_S(277, "mq_timedreceive") +_S(278, "mq_notify") +_S(279, "mq_getsetattr") +_S(280, "waitid") +_S(281, "socket") +_S(282, "bind") +_S(283, "connect") +_S(284, "listen") +_S(285, "accept") +_S(286, "getsockname") +_S(287, "getpeername") +_S(288, "socketpair") +_S(289, "send") +_S(290, "sendto") +_S(291, "recv") +_S(292, "recvfrom") +_S(293, "shutdown") +_S(294, "setsockopt") +_S(295, "getsockopt") +_S(296, "sendmsg") +_S(297, "recvmsg") +_S(298, "semop") +_S(299, "semget") +_S(300, "semctl") +_S(301, "msgsnd") +_S(302, "msgrcv") +_S(303, "msgget") +_S(304, "msgctl") +_S(305, "shmat") +_S(306, "shmdt") +_S(307, "shmget") +_S(308, "shmctl") +_S(309, "add_key") +_S(310, "request_key") +_S(311, "keyctl") +_S(312, "semtimedop") +_S(313, "vserver") +_S(314, "ioprio_set") +_S(315, "ioprio_get") +_S(316, "inotify_init") +_S(317, "inotify_add_watch") +_S(318, "inotify_rm_watch") +_S(319, "mbind") +_S(320, "get_mempolicy") +_S(321, "set_mempolicy") +_S(322, "openat") +_S(323, "mkdirat") +_S(324, "mknodat") +_S(325, "fchownat") +_S(326, "futimesat") +_S(327, "fstatat64") +_S(328, "unlinkat") +_S(329, "renameat") +_S(330, "linkat") +_S(331, "symlinkat") +_S(332, "readlinkat") +_S(333, "fchmodat") +_S(334, "faccessat") +_S(337, "unshare") +_S(338, "set_robust_list") +_S(339, "get_robust_list") +_S(340, "splice") +_S(341, "sync_file_range") +_S(342, "tee") +_S(343, "vmsplice") +_S(344, "move_pages") +_S(345, "getcpu") +_S(347, "kexec_load") +_S(348, "utimensat") +_S(349, "signalfd") +_S(350, "timerfd_create") +_S(351, "eventfd") +_S(352, "fallocate") +_S(353, "timerfd_settime") +_S(354, "timerfd_gettime") +_S(355, "signalfd4") +_S(356, "eventfd2") +_S(357, "epoll_create1") +_S(358, "dup3") +_S(359, "pipe2") +_S(360, "inotify_init1") +_S(361, "preadv") +_S(362, "pwritev") +_S(363, "rt_tgsigqueueinfo") +_S(364, "perf_event_open") +_S(365, "recvmmsg") +_S(366, "accept4") +_S(367, "fanotify_init") +_S(368, "fanotify_mark") +_S(369, "prlimit64") +_S(370, "name_to_handle_at") +_S(371, "open_by_handle_at") +_S(372, "clock_adjtime") +_S(373, "syncfs") +_S(374, "sendmmsg") +_S(375, "setns") +_S(376, "process_vm_readv") +_S(377, "process_vm_writev") +_S(378, "kcmp") +_S(379, "finit_module") +_S(380, "sched_setattr") +_S(381, "sched_getattr") +_S(382, "renameat2") +_S(383, "seccomp") +_S(384, "getrandom") +_S(385, "memfd_create") +_S(386, "bpf") +_S(387, "execveat") diff --git a/framework/src/audit/lib/audit.pc.in b/framework/src/audit/lib/audit.pc.in new file mode 100644 index 00000000..140c919c --- /dev/null +++ b/framework/src/audit/lib/audit.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libaudit +Description: Libraries needed for apps that use the kernel audit framework +Version: @VERSION@ +Libs: -L${libdir} -laudit +Cflags: -I${includedir} diff --git a/framework/src/audit/lib/audit_logging.c b/framework/src/audit/lib/audit_logging.c new file mode 100644 index 00000000..c9461061 --- /dev/null +++ b/framework/src/audit/lib/audit_logging.c @@ -0,0 +1,746 @@ +/* audit_logging.c -- + * Copyright 2005-2008,2010,2011,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include // inet6 addrlen +#include // gethostbyname +#include // inet_ntop +#include +#include // PATH_MAX + +#include "libaudit.h" +#include "private.h" + +#define TTY_PATH 32 +#define MAX_USER (UT_NAMESIZE * 2) + 8 + +// NOTE: The kernel fills in pid, uid, and loginuid of sender. Therefore, +// these routines do not need to send them. + +/* + * resolve's the hostname - caller must pass a INET6_ADDRSTRLEN byte buffer + * Returns string w/ numerical address, or "?" on failure + */ +static void _resolve_addr(char buf[], const char *host) +{ + struct addrinfo *ai; + struct addrinfo hints; + int e; + + buf[0] = '?'; + buf[1] = 0; + /* Short circuit this lookup if NULL, or empty */ + if (host == NULL || *host == 0) + return; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + e = getaddrinfo(host, NULL, &hints, &ai); + if (e != 0) { + audit_msg(LOG_ERR, + "resolve_addr: cannot resolve hostname %s (%s)", + host, gai_strerror(e)); + return; + } + // What to do if more than 1 addr? + inet_ntop(ai->ai_family, ai->ai_family == AF_INET ? + (void *) &((struct sockaddr_in *)ai->ai_addr)->sin_addr : + (void *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, + buf, INET6_ADDRSTRLEN); + freeaddrinfo(ai); +} + +/* + * This function checks a string to see if it needs encoding. It + * return 1 if needed and 0 if not + */ +int audit_value_needs_encoding(const char *str, unsigned int size) +{ + unsigned int i; + + if (str == NULL) + return 0; + + for (i=0; i 0x7f because str[] is signed. + if (str[i] == '"' || str[i] < 0x21 || str[i] == 0x7F) + return 1; + } + return 0; +} + +/* + * This function does encoding of "untrusted" names just like the kernel + */ +char *audit_encode_value(char *final, const char *buf, unsigned int size) +{ + unsigned int i; + char *ptr = final; + const char *hex = "0123456789ABCDEF"; + + if (final == NULL) + return NULL; + + if (buf == NULL) { + *final = 0; + return final; + } + + for (i=0; i>4]; /* Upper nibble */ + *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ + } + *ptr = 0; + return final; +} + +char *audit_encode_nv_string(const char *name, const char *value, + unsigned int vlen) +{ + char *str; + + if (vlen == 0 && value) + vlen = strlen(value); + + if (value && audit_value_needs_encoding(value, vlen)) { + char *tmp = malloc(2*vlen + 1); + if (tmp) { + audit_encode_value(tmp, value, vlen); + if (asprintf(&str, "%s=%s", name, tmp) < 0) + str = NULL; + free(tmp); + } else + str = NULL; + } else + if (asprintf(&str, "%s=\"%s\"", name, value ? value : "?") < 0) + str = NULL; + return str; +} + +/* + * Get the executable's name + */ +static char *_get_exename(char *exename, int size) +{ + int res; + char tmp[PATH_MAX+1]; + + /* get the name of the current executable */ + if ((res = readlink("/proc/self/exe", tmp, PATH_MAX)) == -1) { + strcpy(exename, "\"?\""); + audit_msg(LOG_ERR, "get_exename: cannot determine executable"); + } else { + tmp[res] = '\0'; + if (audit_value_needs_encoding(tmp, res)) + return audit_encode_value(exename, tmp, res); + snprintf(exename, size, "\"%s\"", tmp); + } + return exename; +} + +/* + * Get the command line name + * NOTE: at the moment, this only escapes what the user sent + */ +static char *_get_commname(const char *comm, char *commname, unsigned int size) +{ + unsigned int len; + + if (comm == NULL) { + strcpy(commname, "\"?\""); + return commname; + } + + len = strlen(comm); + if (audit_value_needs_encoding(comm, len)) + audit_encode_value(commname, comm, len); + else + snprintf(commname, size, "\"%s\"", comm); + + return commname; +} + +static int check_ttyname(const char *ttyn) +{ + struct stat statbuf; + + if (lstat(ttyn, &statbuf) + || !S_ISCHR(statbuf.st_mode) + || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))) { + audit_msg(LOG_ERR, "FATAL: bad tty %s", ttyn); + return 1; + } + return 0; +} + +static const char *_get_tty(char *tname, int size) +{ + int rc, i, found = 0; + + for (i=0; i<3 && !found; i++) { + rc = ttyname_r(i, tname, size); + if (rc == 0 && tname[0] != '\0') + found = 1; + } + + if (!found) + return NULL; + + if (check_ttyname(tname)) + return NULL; + + if (strncmp(tname, "/dev/", 5) == 0) + return &tname[5]; + + return tname; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all console apps that do + * not manipulate accounts or groups. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN + * message - the message being sent + * hostname - the hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_message(int audit_fd, int type, const char *message, + const char *hostname, const char *addr, const char *tty, int result) +{ + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2]=""; + char ttyname[TTY_PATH]; + const char *success; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (hostname && *hostname == 0) + hostname = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, hostname); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + snprintf(buf, sizeof(buf), + "%s exe=%s hostname=%s addr=%s terminal=%s res=%s", + message, exename, + hostname ? hostname : "?", + addrbuf, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf ); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all console apps that do + * not manipulate accounts or groups and are executing a script. An example + * would be python or crond wanting to say what they are executing. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN + * message - the message being sent + * comm - the program command line name + * hostname - the hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_comm_message(int audit_fd, int type, const char *message, + const char *comm, const char *hostname, const char *addr, + const char *tty, int result) +{ + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2]=""; + char commname[PATH_MAX*2]; + char ttyname[TTY_PATH]; + const char *success; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (hostname && *hostname == 0) + hostname = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, hostname); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + _get_commname(comm, commname, sizeof(commname)); + + snprintf(buf, sizeof(buf), + "%s comm=%s exe=%s hostname=%s addr=%s terminal=%s res=%s", + message, commname, exename, + hostname ? hostname : "?", + addrbuf, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf ); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + + +/* + * This function will log a message to the audit system using a predefined + * message format. It should be used for all account manipulation operations. + * Parameter usage is as follows: + * + * audit_fd - The fd returned by audit_open + * type - type of message: AUDIT_USER_CHAUTHTOK for changing any account + * attributes. + * pgname - program's name + * op - operation. "adding user", "changing finger info", "deleting group" + * name - user's account or group name. If not available use NULL. + * id - uid or gid that the operation is being performed on. This is used + * only when user is NULL. + * host - The hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_acct_message(int audit_fd, int type, const char *pgname, + const char *op, const char *name, unsigned int id, + const char *host, const char *addr, const char *tty, int result) +{ + const char *success; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2] = ""; + char ttyname[TTY_PATH]; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (host && *host == 0) + host = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, host); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + + if (pgname == NULL) { + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + } else if (pgname[0] != '"') + snprintf(exename, sizeof(exename), "\"%s\"", pgname); + else + snprintf(exename, sizeof(exename), "%s", pgname); + + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + if (name && id == -1) { + char user[MAX_USER]; + const char *format; + size_t len; + + user[0] = 0; + strncat(user, name, MAX_USER-1); + len = strnlen(user, UT_NAMESIZE); + user[len] = 0; + if (audit_value_needs_encoding(name, len)) { + audit_encode_value(user, name, len); + format = + "op=%s acct=%s exe=%s hostname=%s addr=%s terminal=%s res=%s"; + } else + format = + "op=%s acct=\"%s\" exe=%s hostname=%s addr=%s terminal=%s res=%s"; + + snprintf(buf, sizeof(buf), format, + op, user, exename, + host ? host : "?", + addrbuf, + tty ? tty : "?", + success + ); + } else + snprintf(buf, sizeof(buf), + "op=%s id=%u exe=%s hostname=%s addr=%s terminal=%s res=%s", + op, id, exename, + host ? host : "?", + addrbuf, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message(audit_fd, type, REAL_ERR, buf); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all apps that are SE Linux + * object managers. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN + * message - the message being sent + * hostname - the hostname if known + * addr - The network address of the user + * tty - The tty of the user + * uid - The auid of the person related to the avc message + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_avc_message(int audit_fd, int type, const char *message, + const char *hostname, const char *addr, const char *tty, uid_t uid) +{ + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2] = ""; + char ttyname[TTY_PATH]; + int retval; + + if (audit_fd < 0) + return 0; + + /* If hostname is empty string, make it NULL ptr */ + if (hostname && *hostname == 0) + hostname = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, hostname); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + snprintf(buf, sizeof(buf), + "%s exe=%s sauid=%d hostname=%s addr=%s terminal=%s", + message, exename, uid, + hostname ? hostname : "?", + addrbuf, + tty ? tty : "?" + ); + + errno = 0; + retval = audit_send_user_message( audit_fd, type, REAL_ERR, buf ); + if (retval == -EPERM && getuid() != 0) { + syslog(LOG_ERR, "Can't send to audit system: %s %s", + audit_msg_type_to_name(type), buf); + return 0; + } + if ((retval < 1) && errno == 0) + errno = retval; + return retval; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. It should be used for all SE linux user and role + * manipulation operations. + * Parameter usage is as follows: + * + * type - type of message: AUDIT_ROLE_ASSIGN/REMOVE for changing any SE Linux + * user or role attributes. + * pgname - program's name + * op - operation. "adding-user", "adding-role", "deleting-user", "deleting-role" + * name - user's account. If not available use NULL. + * id - uid that the operation is being performed on. This is used + * only when name is NULL. + * new_seuser - the new seuser that the login user is getting + * new_role - the new_role that the login user is getting + * new_range - the new mls range that the login user is getting + * old_seuser - the old seuser that the login usr had + * old_role - the old role that the login user had + * old_range - the old mls range that the login usr had + * host - The hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_semanage_message(int audit_fd, int type, const char *pgname, + const char *op, const char *name, unsigned int id, + const char *new_seuser, const char *new_role, const char *new_range, + const char *old_seuser, const char *old_role, const char *old_range, + const char *host, const char *addr, + const char *tty, int result) +{ + const char *success; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2] = ""; + char ttyname[TTY_PATH]; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (host && *host == 0) + host = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, host); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + + if (pgname == NULL || strlen(pgname) == 0) { + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + pgname = exename; + } + if (tty == NULL || strlen(tty) == 0) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + if (name && strlen(name) > 0) { + size_t len; + const char *format; + char user[MAX_USER]; + + user[0] = 0; + strncat(user, name, MAX_USER-1); + len = strnlen(user, UT_NAMESIZE); + user[len] = 0; + if (audit_value_needs_encoding(name, len)) { + audit_encode_value(user, name, len); + format = "op=%s acct=%s old-seuser=%s old-role=%s old-range=%s new-seuser=%s new-role=%s new-range=%s exe=%s hostname=%s addr=%s terminal=%s res=%s"; + } else + format = "op=%s acct=\"%s\" old-seuser=%s old-role=%s old-range=%s new-seuser=%s new-role=%s new-range=%s exe=%s hostname=%s addr=%s terminal=%s res=%s"; + snprintf(buf, sizeof(buf), format, op, user, + old_seuser && strlen(old_seuser) ? old_seuser : "?", + old_role && strlen(old_role) ? old_role : "?", + old_range && strlen(old_range) ? old_range : "?", + new_seuser && strlen(new_seuser) ? new_seuser : "?", + new_role && strlen(new_role) ? new_role : "?", + new_range && strlen(new_range) ? new_range : "?", + pgname, + host && strlen(host) ? host : "?", + addrbuf, + tty && strlen(tty) ? tty : "?", + success + ); + } else + snprintf(buf, sizeof(buf), + "op=%s id=%u old-seuser=%s old-role=%s old-range=%s new-seuser=%s new-role=%s new-range=%s exe=%s hostname=%s addr=%s terminal=%s res=%s", + op, id, + old_seuser && strlen(old_seuser) ? old_seuser : "?", + old_role && strlen(old_role) ? old_role : "?", + old_range && strlen(old_range) ? old_range : "?", + new_seuser && strlen(new_seuser) ? new_seuser : "?", + new_role && strlen(new_role) ? new_role : "?", + new_range && strlen(new_range) ? new_range : "?", + pgname, + host && strlen(host) ? host : "?", + addrbuf, + tty && strlen(tty) ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message(audit_fd, type, REAL_ERR, buf); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all console apps that do + * not manipulate accounts or groups. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER_CMD + * command - the command line being logged + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_command(int audit_fd, int type, const char *command, + const char *tty, int result) +{ + char *p; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char commname[PATH_MAX*2]; + char cwdname[PATH_MAX*2]; + char ttyname[TTY_PATH]; + char format[64]; + const char *success; + char *cmd; + int ret, cwdenc=0, cmdenc=0; + unsigned int len; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + /* Trim leading spaces */ + while (*command == ' ') + command++; + + cmd = strdup(command); + if (cmd == NULL) + return -1; + + // We borrow the commname buffer + if (getcwd(commname, PATH_MAX) == NULL) + strcpy(commname, "?"); + len = strlen(commname); + if (audit_value_needs_encoding(commname, len)) { + audit_encode_value(cwdname, commname, len); + cwdenc = 1; + } else + strcpy(cwdname, commname); + + len = strlen(cmd); + // Trim the trailing carriage return and spaces + while (len && (cmd[len-1] == 0x0A || cmd[len-1] == ' ')) { + cmd[len-1] = 0; + len--; + } + + if (len >= PATH_MAX) { + cmd[PATH_MAX] = 0; + len = PATH_MAX-1; + } + if (audit_value_needs_encoding(cmd, len)) { + audit_encode_value(commname, cmd, len); + cmdenc = 1; + } + if (cmdenc == 0) + strcpy(commname, cmd); + free(cmd); + + // Make the format string + if (cwdenc) + p=stpcpy(format, "cwd=%s "); + else + p=stpcpy(format, "cwd=\"%s\" "); + + if (cmdenc) + p = stpcpy(p, "cmd=%s "); + else + p = stpcpy(p, "cmd=\"%s\" "); + + strcpy(p, "terminal=%s res=%s"); + + // now use the format string to make the event + snprintf(buf, sizeof(buf), format, + cwdname, commname, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf ); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + diff --git a/framework/src/audit/lib/deprecated.c b/framework/src/audit/lib/deprecated.c new file mode 100644 index 00000000..2238e791 --- /dev/null +++ b/framework/src/audit/lib/deprecated.c @@ -0,0 +1,77 @@ +/* deprecated.c -- This file is the trash heap of things about to leave + * Copyright 2006-07,2009 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "libaudit.h" +#include "private.h" + +/* + * This function will send a user space message to the kernel. + * It returns the sequence number which is > 0 on success + * or <= 0 on error. (pam uses this) This is the main audit sending + * function now. + */ +int audit_send_user_message(int fd, int type, hide_t hide_error, + const char *message) +{ + int retry_cnt = 0; + int rc; +retry: + rc = audit_send(fd, type, message, strlen(message)+1); + if (rc == -ECONNREFUSED) { + /* This is here to let people that build their own kernel + and disable the audit system get in. ECONNREFUSED is + issued by the kernel when there is "no on listening". */ + return 0; + } else if (rc == -EPERM && getuid() != 0 && hide_error == HIDE_IT) { + /* If we get this, then the kernel supports auditing + * but we don't have enough privilege to write to the + * socket. Therefore, we have already been authenticated + * and we are a common user. Just act as though auditing + * is not enabled. Any other error we take seriously. + * This is here basically to satisfy Xscreensaver. */ + return 0; + } else if (rc == -EINVAL) { + /* If we get this, the kernel doesn't understand the + * netlink message type. This is most likely due to + * being an old kernel. Use the old message type. */ + if (type >= AUDIT_FIRST_USER_MSG && + type <= AUDIT_LAST_USER_MSG && !retry_cnt) { + + /* do retry */ + type = AUDIT_USER; + retry_cnt++; + goto retry; + } + } + return rc; +} +hidden_def(audit_send_user_message) + diff --git a/framework/src/audit/lib/dso.h b/framework/src/audit/lib/dso.h new file mode 100644 index 00000000..662b521b --- /dev/null +++ b/framework/src/audit/lib/dso.h @@ -0,0 +1,45 @@ +/* dso.h -- + * Copyright 2005,2006,2009 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +#ifndef _DSO_H_ +#define _DSO_H_ + +#ifdef PIC +# define hidden __attribute__ ((visibility ("hidden"))) +# define hidden_proto(fct) __hidden_proto (fct, fct##_internal) +# define __hidden_proto(fct, internal) \ + extern __typeof (fct) internal; \ + extern __typeof (fct) fct __asm (#internal) hidden; +# if defined(__alpha__) || defined(__mips__) +# define hidden_def(fct) \ + asm (".globl " #fct "\n" #fct " = " #fct "_internal"); +# else +# define hidden_def(fct) \ + asm (".globl " #fct "\n.set " #fct ", " #fct "_internal"); +#endif +#else +# define hidden +# define hidden_proto(fct) +# define hidden_def(fct) +#endif + +#endif + diff --git a/framework/src/audit/lib/errormsg.h b/framework/src/audit/lib/errormsg.h new file mode 100644 index 00000000..a4fea664 --- /dev/null +++ b/framework/src/audit/lib/errormsg.h @@ -0,0 +1,66 @@ +/* errormsg.h -- + * Copyright 2008 FUJITSU Inc. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Zhang Xiliang + */ + +struct msg_tab { + int key; /* error number */ + /* + * the field string position in the error message + * 0: don't output field string + * 1: output field string before error message + * 2: output field string after error message + */ + int position; + const char *cvalue; +}; + +#ifndef NO_TABLES +static const struct msg_tab err_msgtab[] = { + { -1, 2, "-F missing operation for" }, + { -2, 2, "-F unknown field:" }, + { -3, 1, "must be before -S" }, + { -4, 1, "machine type not found" }, + { -5, 1, "elf mapping not found" }, + { -6, 1, "requested bit level not supported by machine" }, + { -7, 1, "can only be used with exit filter list" }, + { -8, 2, "-F unknown message type -" }, + { -9, 0, "msgtype field can only be used with exclude filter list" }, + { -10, 0, "Failed upgrading rule" }, + { -11, 0, "String value too long" }, + { -12, 0, "Only msgtype field can be used with exclude filter" }, + { -13, 1, "only takes = or != operators" }, + { -14, 0, "Permission can only contain \'rwxa\'" }, + { -15, 2, "-F unknown errno -"}, + { -16, 2, "-F unknown file type - " }, + { -17, 1, "can only be used with exit and entry filter list" }, + { -18, 1, "" }, // Unused + { -19, 0, "Key field needs a watch or syscall given prior to it" }, + { -20, 2, "-F missing value after operation for" }, + { -21, 2, "-F value should be number for" }, + { -22, 2, "-F missing field name before operator for" }, + { -23, 2, "" }, // Unused + { -24, 2, "-C missing field name before operator for" }, + { -25, 2, "-C missing value after operation for "}, + { -26, 2, "-C unknown field:" }, + { -27, 2, "-C unknown right hand value for comparison with:" }, + { -28, 2, "Too many fields in rule" }, +}; +#endif diff --git a/framework/src/audit/lib/errtab.h b/framework/src/audit/lib/errtab.h new file mode 100644 index 00000000..f777d79d --- /dev/null +++ b/framework/src/audit/lib/errtab.h @@ -0,0 +1,154 @@ +/* errtab.h -- + * Copyright 2007 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(EPERM, "EPERM" ) +_S(ENOENT, "ENOENT" ) +_S(ESRCH, "ESRCH" ) +_S(EINTR, "EINTR" ) +_S(EIO, "EIO" ) +_S(ENXIO, "ENXIO" ) +_S(E2BIG, "E2BIG" ) +_S(ENOEXEC, "ENOEXEC" ) +_S(EBADF, "EBADF" ) +_S(ECHILD, "ECHILD" ) +_S(EAGAIN, "EAGAIN" ) +_S(ENOMEM, "ENOMEM" ) +_S(EACCES, "EACCES" ) +_S(EFAULT, "EFAULT" ) +_S(ENOTBLK, "ENOTBLK" ) +_S(EBUSY, "EBUSY" ) +_S(EEXIST, "EEXIST" ) +_S(EXDEV, "EXDEV" ) +_S(ENODEV, "ENODEV" ) +_S(ENOTDIR, "ENOTDIR" ) +_S(EISDIR, "EISDIR" ) +_S(EINVAL, "EINVAL" ) +_S(ENFILE, "ENFILE" ) +_S(EMFILE, "EMFILE" ) +_S(ENOTTY, "ENOTTY" ) +_S(ETXTBSY, "ETXTBSY" ) +_S(EFBIG, "EFBIG" ) +_S(ENOSPC, "ENOSPC" ) +_S(ESPIPE, "ESPIPE" ) +_S(EROFS, "EROFS" ) +_S(EMLINK, "EMLINK" ) +_S(EPIPE, "EPIPE" ) +_S(EDOM, "EDOM" ) +_S(ERANGE, "ERANGE" ) +_S(EDEADLK, "EDEADLK" ) +_S(ENAMETOOLONG, "ENAMETOOLONG" ) +_S(ENOLCK, "ENOLCK" ) +_S(ENOSYS, "ENOSYS" ) +_S(ENOTEMPTY, "ENOTEMPTY" ) +_S(ELOOP, "ELOOP" ) +_S(EWOULDBLOCK, "EWOULDBLOCK" ) +_S(ENOMSG, "ENOMSG" ) +_S(EIDRM, "EIDRM" ) +_S(ECHRNG, "ECHRNG" ) +_S(EL2NSYNC, "EL2NSYNC" ) +_S(EL3HLT, "EL3HLT" ) +_S(EL3RST, "EL3RST" ) +_S(ELNRNG, "ELNRNG" ) +_S(EUNATCH, "EUNATCH" ) +_S(ENOCSI, "ENOCSI" ) +_S(EL2HLT, "EL2HLT" ) +_S(EBADE, "EBADE" ) +_S(EBADR, "EBADR" ) +_S(EXFULL, "EXFULL" ) +_S(ENOANO, "ENOANO" ) +_S(EBADRQC, "EBADRQC" ) +_S(EBADSLT, "EBADSLT" ) +_S(EDEADLOCK, "EDEADLOCK" ) +_S(EBFONT, "EBFONT" ) +_S(ENOSTR, "ENOSTR" ) +_S(ENODATA, "ENODATA" ) +_S(ETIME, "ETIME" ) +_S(ENOSR, "ENOSR" ) +_S(ENONET, "ENONET" ) +_S(ENOPKG, "ENOPKG" ) +_S(EREMOTE, "EREMOTE" ) +_S(ENOLINK, "ENOLINK" ) +_S(EADV, "EADV" ) +_S(ESRMNT, "ESRMNT" ) +_S(ECOMM, "ECOMM" ) +_S(EPROTO, "EPROTO" ) +_S(EMULTIHOP, "EMULTIHOP" ) +_S(EDOTDOT, "EDOTDOT" ) +_S(EBADMSG, "EBADMSG" ) +_S(EOVERFLOW, "EOVERFLOW" ) +_S(ENOTUNIQ, "ENOTUNIQ" ) +_S(EBADFD, "EBADFD" ) +_S(EREMCHG, "EREMCHG" ) +_S(ELIBACC, "ELIBACC" ) +_S(ELIBBAD, "ELIBBAD" ) +_S(ELIBSCN, "ELIBSCN" ) +_S(ELIBMAX, "ELIBMAX" ) +_S(ELIBEXEC, "ELIBEXEC" ) +_S(EILSEQ, "EILSEQ" ) +_S(ERESTART, "ERESTART" ) +_S(ESTRPIPE, "ESTRPIPE" ) +_S(EUSERS, "EUSERS" ) +_S(ENOTSOCK, "ENOTSOCK" ) +_S(EDESTADDRREQ, "EDESTADDRREQ" ) +_S(EMSGSIZE, "EMSGSIZE" ) +_S(EPROTOTYPE, "EPROTOTYPE" ) +_S(ENOPROTOOPT, "ENOPROTOOPT" ) +_S(EPROTONOSUPPORT, "EPROTONOSUPPORT" ) +_S(ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT" ) +_S(EOPNOTSUPP, "EOPNOTSUPP" ) +_S(EPFNOSUPPORT, "EPFNOSUPPORT" ) +_S(EAFNOSUPPORT, "EAFNOSUPPORT" ) +_S(EADDRINUSE, "EADDRINUSE" ) +_S(EADDRNOTAVAIL, "EADDRNOTAVAIL" ) +_S(ENETDOWN, "ENETDOWN" ) +_S(ENETUNREACH, "ENETUNREACH" ) +_S(ENETRESET, "ENETRESET" ) +_S(ECONNABORTED, "ECONNABORTED" ) +_S(ECONNRESET, "ECONNRESET" ) +_S(ENOBUFS, "ENOBUFS" ) +_S(EISCONN, "EISCONN" ) +_S(ENOTCONN, "ENOTCONN" ) +_S(ESHUTDOWN, "ESHUTDOWN" ) +_S(ETOOMANYREFS, "ETOOMANYREFS" ) +_S(ETIMEDOUT, "ETIMEDOUT" ) +_S(ECONNREFUSED, "ECONNREFUSED" ) +_S(EHOSTDOWN, "EHOSTDOWN" ) +_S(EHOSTUNREACH, "EHOSTUNREACH" ) +_S(EALREADY, "EALREADY" ) +_S(EINPROGRESS, "EINPROGRESS" ) +_S(ESTALE, "ESTALE" ) +_S(EUCLEAN, "EUCLEAN" ) +_S(ENOTNAM, "ENOTNAM" ) +_S(ENAVAIL, "ENAVAIL" ) +_S(EISNAM, "EISNAM" ) +_S(EREMOTEIO, "EREMOTEIO" ) +_S(EDQUOT, "EDQUOT" ) +_S(ENOMEDIUM, "ENOMEDIUM" ) +_S(EMEDIUMTYPE, "EMEDIUMTYPE" ) +_S(ECANCELED, "ECANCELED" ) +_S(ENOKEY, "ENOKEY" ) +_S(EKEYEXPIRED, "EKEYEXPIRED" ) +_S(EKEYREVOKED, "EKEYREVOKED" ) +_S(EKEYREJECTED, "EKEYREJECTED" ) +_S(EOWNERDEAD, "EOWNERDEAD" ) +_S(ENOTRECOVERABLE, "ENOTRECOVERABLE" ) + diff --git a/framework/src/audit/lib/fieldtab.h b/framework/src/audit/lib/fieldtab.h new file mode 100644 index 00000000..dd7474c1 --- /dev/null +++ b/framework/src/audit/lib/fieldtab.h @@ -0,0 +1,68 @@ +/* fieldtab.h -- + * Copyright 2005-07 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(AUDIT_PID, "pid" ) +_S(AUDIT_UID, "uid" ) +_S(AUDIT_EUID, "euid" ) +_S(AUDIT_SUID, "suid" ) +_S(AUDIT_FSUID, "fsuid" ) +_S(AUDIT_GID, "gid" ) +_S(AUDIT_EGID, "egid" ) +_S(AUDIT_SGID, "sgid" ) +_S(AUDIT_FSGID, "fsgid" ) +_S(AUDIT_LOGINUID, "auid" ) +_S(AUDIT_LOGINUID, "loginuid" ) +_S(AUDIT_PERS, "pers" ) +_S(AUDIT_ARCH, "arch" ) +_S(AUDIT_MSGTYPE, "msgtype" ) +_S(AUDIT_SUBJ_USER, "subj_user" ) +_S(AUDIT_SUBJ_ROLE, "subj_role" ) +_S(AUDIT_SUBJ_TYPE, "subj_type" ) +_S(AUDIT_SUBJ_SEN, "subj_sen" ) +_S(AUDIT_SUBJ_CLR, "subj_clr" ) +_S(AUDIT_PPID, "ppid" ) +_S(AUDIT_OBJ_USER, "obj_user" ) +_S(AUDIT_OBJ_ROLE, "obj_role" ) +_S(AUDIT_OBJ_TYPE, "obj_type" ) +_S(AUDIT_OBJ_LEV_LOW, "obj_lev_low" ) +_S(AUDIT_OBJ_LEV_HIGH, "obj_lev_high" ) + +_S(AUDIT_DEVMAJOR, "devmajor" ) +_S(AUDIT_DEVMINOR, "devminor" ) +_S(AUDIT_INODE, "inode" ) +_S(AUDIT_EXIT, "exit" ) +_S(AUDIT_SUCCESS, "success" ) +_S(AUDIT_WATCH, "path" ) +_S(AUDIT_PERM, "perm" ) +_S(AUDIT_DIR, "dir" ) +_S(AUDIT_FILETYPE, "filetype" ) +_S(AUDIT_OBJ_UID, "obj_uid" ) +_S(AUDIT_OBJ_GID, "obj_gid" ) +_S(AUDIT_FIELD_COMPARE, "field_compare" ) + +_S(AUDIT_ARG0, "a0" ) +_S(AUDIT_ARG1, "a1" ) +_S(AUDIT_ARG2, "a2" ) +_S(AUDIT_ARG3, "a3" ) + +_S(AUDIT_FILTERKEY, "key" ) + diff --git a/framework/src/audit/lib/flagtab.h b/framework/src/audit/lib/flagtab.h new file mode 100644 index 00000000..e08d9bca --- /dev/null +++ b/framework/src/audit/lib/flagtab.h @@ -0,0 +1,26 @@ +/* flagtab.h -- + * Copyright 2005,2006 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +_S(AUDIT_FILTER_TASK, "task" ) +_S(AUDIT_FILTER_ENTRY, "entry" ) +_S(AUDIT_FILTER_EXIT, "exit" ) +_S(AUDIT_FILTER_USER, "user" ) +_S(AUDIT_FILTER_EXCLUDE, "exclude" ) diff --git a/framework/src/audit/lib/ftypetab.h b/framework/src/audit/lib/ftypetab.h new file mode 100644 index 00000000..e1dc2676 --- /dev/null +++ b/framework/src/audit/lib/ftypetab.h @@ -0,0 +1,30 @@ +/* actiontab.h -- + * Copyright 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(S_IFSOCK, "socket" ) +_S(S_IFLNK, "link" ) +_S(S_IFREG, "file" ) +_S(S_IFBLK, "block" ) +_S(S_IFDIR, "dir" ) +_S(S_IFCHR, "character" ) +_S(S_IFIFO, "fifo" ) + diff --git a/framework/src/audit/lib/gen_tables.c b/framework/src/audit/lib/gen_tables.c new file mode 100644 index 00000000..9f25b506 --- /dev/null +++ b/framework/src/audit/lib/gen_tables.c @@ -0,0 +1,418 @@ +/* gen_tables.c -- Generator of lookup tables. + * Copyright 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Miloslav Trmač + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef MS_DIRSYNC +#include +#endif +#include "gen_tables.h" +#include "libaudit.h" +#include "interpret.h" + +/* This is from asm/ipc.h. Copying it for now as some platforms + * * have broken headers. */ +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define SEMTIMEDOP 4 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 + + +/* The ratio of table size to number of non-empty elements allowed for a + "direct" s2i table; if the ratio would be bigger, bsearch tables are used + instead. + + 2 looks like a lot at a first glance, but the bsearch tables need twice as + much space per element, so with the ratio equal to 2 the direct table uses + no more memory and is faster. */ +#define DIRECT_THRESHOLD 2 + +/* Allow more than one string defined for a single integer value */ +static bool allow_duplicate_ints; /* = false; */ + +struct value { + int val; + const char *s; + size_t s_offset; + size_t orig_index; +}; + +/* The mapping to store. */ +static struct value values[] = { +#define _S(VAL, S) { (VAL), (S), 0, 0 }, +#include TABLE_H +#undef _S +}; + +#define NUM_VALUES (sizeof(values) / sizeof(*values)) + +/* Compare two "struct value" members by name. */ +static int +cmp_value_strings(const void *xa, const void *xb) +{ + const struct value *a, *b; + + a = xa; + b = xb; + return strcmp(a->s, b->s); +} + +/* Compare two "struct value" members by value. */ +static int +cmp_value_vals(const void *xa, const void *xb) +{ + const struct value *a, *b; + + a = xa; + b = xb; + if (a->val > b->val) + return 1; + if (a->val < b->val) + return -1; + /* Preserve the original order if there is an ambiguity, to always use + the first specified value. */ + if (a->orig_index > b->orig_index) + return 1; + if (a->orig_index < b->orig_index) + return -1; + return 0; +} + +/* Compare two "struct value" members by orig_index. */ +static int +cmp_value_orig_index(const void *xa, const void *xb) +{ + const struct value *a, *b; + + a = xa; + b = xb; + if (a->orig_index > b->orig_index) + return 1; + if (a->orig_index < b->orig_index) + return -1; + return 0; +} + +/* Output the string table, initialize values[*]->s_offset. */ +static void +output_strings(const char *prefix) +{ + size_t i, offset; + + offset = 0; + for (i = 0; i < NUM_VALUES; i++) { + values[i].s_offset = offset; + offset += strlen(values[i].s) + 1; + } + printf("static const char %s_strings[] = \"", prefix); + assert(NUM_VALUES > 0); + for (i = 0; i < NUM_VALUES; i++) { + const char *c; + + if (i != 0 && i % 10 == 0) + fputs("\"\n" + "\t\"", stdout); + for (c = values[i].s; *c != '\0'; c++) { + assert(*c != '"' && *c != '\\' + && isprint((unsigned char)*c)); + putc(*c, stdout); + } + if (i != NUM_VALUES - 1) + fputs("\\0", stdout); + } + fputs("\";\n", stdout); +} + +/* Output the string to integer mapping code. + Assume strings are all uppsercase or all lowercase if specified by + parameters; in that case, make the search case-insensitive. + values must be sorted by strings. */ +static void +output_s2i(const char *prefix, bool uppercase, bool lowercase) +{ + size_t i; + + for (i = 0; i < NUM_VALUES - 1; i++) { + assert(strcmp(values[i].s, values[i + 1].s) <= 0); + if (strcmp(values[i].s, values[i + 1].s) == 0) { + fprintf(stderr, "Duplicate value `%s': %d, %d\n", + values[i].s, values[i].val, values[i + 1].val); + abort(); + } + } + printf("static const unsigned %s_s2i_s[] = {", prefix); + for (i = 0; i < NUM_VALUES; i++) { + if (i % 10 == 0) + fputs("\n\t", stdout); + assert(values[i].s_offset <= UINT_MAX); + printf("%zu,", values[i].s_offset); + } + printf("\n" + "};\n" + "static const int %s_s2i_i[] = {", prefix); + for (i = 0; i < NUM_VALUES; i++) { + if (i % 10 == 0) + fputs("\n\t", stdout); + printf("%d,", values[i].val); + } + fputs("\n" + "};\n", stdout); + assert(!(uppercase && lowercase)); + if (uppercase) { + for (i = 0; i < NUM_VALUES; i++) { + const char *c; + + for (c = values[i].s; *c != '\0'; c++) + assert(isascii((unsigned char)*c) + && !GT_ISLOWER(*c)); + } + } else if (lowercase) { + for (i = 0; i < NUM_VALUES; i++) { + const char *c; + + for (c = values[i].s; *c != '\0'; c++) + assert(isascii((unsigned char)*c) + && !GT_ISUPPER(*c)); + } + } + if (uppercase || lowercase) { + printf("static int %s_s2i(const char *s, int *value) {\n" + "\tsize_t len, i;\n" + "\tlen = strlen(s);\n" + "\t{ char copy[len + 1];\n" + "\tfor (i = 0; i < len; i++) {\n" + "\t\tchar c = s[i];\n", prefix); + if (uppercase) + fputs("\t\tcopy[i] = GT_ISLOWER(c) ? c - 'a' + 'A' " + ": c;\n", stdout); + else + fputs("\t\tcopy[i] = GT_ISUPPER(c) ? c - 'A' + 'a' " + ": c;\n", stdout); + printf("\t}\n" + "\tcopy[i] = 0;\n" + "\treturn s2i__(%s_strings, %s_s2i_s, %s_s2i_i, %zu, " + "copy, value);\n" + "\t}\n" + "}\n", prefix, prefix, prefix, NUM_VALUES); + } else + printf("static int %s_s2i(const char *s, int *value) {\n" + "\treturn s2i__(%s_strings, %s_s2i_s, %s_s2i_i, %zu, s, " + "value);\n" + "}\n", prefix, prefix, prefix, prefix, NUM_VALUES); +} + +/* Output the string to integer mapping table. + values must be sorted by strings. */ +static void +output_i2s(const char *prefix) +{ + struct value *unique_values; + int min_val, max_val; + size_t i, n; + + assert(NUM_VALUES > 0); + for (i = 0; i < NUM_VALUES - 1; i++) { + assert(values[i].val <= values[i + 1].val); + if (!allow_duplicate_ints + && values[i].val == values[i + 1].val) { + fprintf(stderr, "Duplicate value %d: `%s', `%s'\n", + values[i].val, values[i].s, values[i + 1].s); + abort(); + } + } + + unique_values = malloc(NUM_VALUES * sizeof(*unique_values)); + assert(unique_values != NULL); + n = 0; + for (i = 0; i < NUM_VALUES; i++) { + if (n == 0 || unique_values[n - 1].val != values[i].val) { + unique_values[n] = values[i]; + n++; + } + } + + min_val = unique_values[0].val; + max_val = unique_values[n - 1].val; + if (((double)max_val - (double)min_val) / n <= DIRECT_THRESHOLD) { + int next_index; + + printf("static const unsigned %s_i2s_direct[] = {", prefix); + next_index = min_val; + i = 0; + for (;;) { + if ((next_index - min_val) % 10 == 0) + fputs("\n\t", stdout); + while (unique_values[i].val < next_index) + /* This can happen if (allow_duplicate_ints) */ + i++; + if (unique_values[i].val == next_index) { + assert(unique_values[i].s_offset <= UINT_MAX); + printf("%zu,", unique_values[i].s_offset); + } else + fputs("-1u,", stdout); + if (next_index == max_val) + /* Done like this to avoid integer overflow */ + break; + next_index++; + } + printf("\n" + "};\n" + "static const char *%s_i2s(int v) {\n" + "\treturn i2s_direct__(%s_strings, %s_i2s_direct, %d, " + "%d, v);\n" + "}\n", prefix, prefix, prefix, min_val, max_val); + } else { + printf("static const int %s_i2s_i[] = {", prefix); + for (i = 0; i < n; i++) { + if (i % 10 == 0) + fputs("\n\t", stdout); + printf("%d,", unique_values[i].val); + } + printf("\n" + "};\n" + "static const unsigned %s_i2s_s[] = {", prefix); + for (i = 0; i < n; i++) { + if (i % 10 == 0) + fputs("\n\t", stdout); + assert(unique_values[i].s_offset <= UINT_MAX); + printf("%zu,", unique_values[i].s_offset); + } + printf("\n" + "};\n" + "static const char *%s_i2s(int v) {\n" + "\treturn i2s_bsearch__(%s_strings, %s_i2s_i, %s_i2s_s, " + "%zu, v);\n" + "}\n", prefix, prefix, prefix, prefix, n); + } + free(unique_values); +} + +/* Output the string to integer mapping table as a transtab[]. + values must be sorted in the desired order. */ +static void +output_i2s_transtab(const char *prefix) +{ + size_t i; + char *uc_prefix; + + printf("static const struct transtab %s_table[] = {", prefix); + for (i = 0; i < NUM_VALUES; i++) { + if (i % 10 == 0) + fputs("\n\t", stdout); + printf("{%d,%zu},", values[i].val, values[i].s_offset); + } + uc_prefix = strdup(prefix); + assert(uc_prefix != NULL); + for (i = 0; uc_prefix[i] != '\0'; i++) + uc_prefix[i] = toupper((unsigned char)uc_prefix[i]); + printf("\n" + "};\n" + "#define %s_NUM_ENTRIES " + "(sizeof(%s_table) / sizeof(*%s_table))\n", uc_prefix, prefix, + prefix); + free(uc_prefix); +} + +int +main(int argc, char **argv) +{ + bool gen_i2s, gen_i2s_transtab, gen_s2i, uppercase, lowercase; + char *prefix; + size_t i; + + /* This is required by gen_tables.h */ + assert(NUM_VALUES <= (SSIZE_MAX / 2 + 1)); + + /* To make sure GT_ISUPPER and GT_ISLOWER work. */ + assert('Z' == 'A' + 25 && 'z' == 'a' + 25); + gen_i2s = false; + gen_i2s_transtab = false; + gen_s2i = false; + uppercase = false; + lowercase = false; + prefix = NULL; + assert (argc > 1); + for (i = 1; i < (size_t)argc; i++) { + if (strcmp(argv[i], "--i2s") == 0) + gen_i2s = true; + else if (strcmp(argv[i], "--i2s-transtab") == 0) + gen_i2s_transtab = true; + else if (strcmp(argv[i], "--s2i") == 0) + gen_s2i = true; + else if (strcmp(argv[i], "--uppercase") == 0) + uppercase = true; + else if (strcmp(argv[i], "--lowercase") == 0) + lowercase = true; + else if (strcmp(argv[i], "--duplicate-ints") == 0) + allow_duplicate_ints = true; + else { + assert(*argv[i] != '-'); + assert(prefix == NULL); + prefix = argv[i]; + } + } + assert(prefix != NULL); + assert(!(uppercase && lowercase)); + + printf("/* This is a generated file, see Makefile.am for its " + "inputs. */\n"); + for (i = 0; i < NUM_VALUES; i++) + values[i].orig_index = i; + qsort(values, NUM_VALUES, sizeof(*values), cmp_value_strings); + /* FIXME? if (gen_s2i), sort the strings in some other order + (e.g. "first 4 nodes in BFS of the bsearch tree first") to use the + cache better. */ + /* FIXME? If the only thing generated is a transtab, keep the strings + in the original order to use the cache better. */ + output_strings(prefix); + if (gen_s2i) + output_s2i(prefix, uppercase, lowercase); + if (gen_i2s) { + qsort(values, NUM_VALUES, sizeof(*values), cmp_value_vals); + output_i2s(prefix); + } + if (gen_i2s_transtab) { + qsort(values, NUM_VALUES, sizeof(*values), + cmp_value_orig_index); + output_i2s_transtab(prefix); + } + return EXIT_SUCCESS; +} diff --git a/framework/src/audit/lib/gen_tables.h b/framework/src/audit/lib/gen_tables.h new file mode 100644 index 00000000..84237a28 --- /dev/null +++ b/framework/src/audit/lib/gen_tables.h @@ -0,0 +1,102 @@ +/* gen_tables.h -- Declarations used for lookup tables. + * Copyright 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Miloslav Trmač + */ +#ifndef GEN_TABLES_H__ +#define GEN_TABLES_H__ + +#include +#include + +/* Assumes ASCII; verified in gen_tables.c. */ +#define GT_ISUPPER(X) ((X) >= 'A' && (X) <= 'Z') +#define GT_ISLOWER(X) ((X) >= 'a' && (X) <= 'z') + +inline static int s2i__(const char *strings, const unsigned *s_table, + const int *i_table, size_t n, const char *s, int *value) +{ + ssize_t left, right; + + left = 0; + right = n - 1; + while (left <= right) { /* invariant: left <= x <= right */ + size_t mid; + int r; + + mid = (left + right) / 2; + /* FIXME? avoid recomparing a common prefix */ + r = strcmp(s, strings + s_table[mid]); + if (r == 0) { + *value = i_table[mid]; + return 1; + } + if (r < 0) + right = mid - 1; + else + left = mid + 1; + } + return 0; +} + +inline static const char *i2s_direct__(const char *strings, + const unsigned *table, int min, int max, + int v) +{ + unsigned off; + + if (v < min || v > max) + return NULL; + off = table[v - min]; + if (off != -1u) + return strings + off; + return NULL; +} + +inline static const char *i2s_bsearch__(const char *strings, + const int *i_table, + const unsigned *s_table, size_t n, + int v) +{ + ssize_t left, right; + + left = 0; + right = n - 1; + while (left <= right) { /* invariant: left <= x <= right */ + size_t mid; + int mid_val; + + mid = (left + right) / 2; + mid_val = i_table[mid]; + if (v == mid_val) + return strings + s_table[mid]; + if (v < mid_val) + right = mid - 1; + else + left = mid + 1; + } + return NULL; +} + +struct transtab { + int value; + unsigned offset; +}; + +#endif diff --git a/framework/src/audit/lib/i386_table.h b/framework/src/audit/lib/i386_table.h new file mode 100644 index 00000000..208310cf --- /dev/null +++ b/framework/src/audit/lib/i386_table.h @@ -0,0 +1,379 @@ +/* i386_table.h -- + * Copyright 2005-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(0, "restart_syscall") +_S(1, "exit") +_S(2, "fork") +_S(3, "read") +_S(4, "write") +_S(5, "open") +_S(6, "close") +_S(7, "waitpid") +_S(8, "creat") +_S(9, "link") +_S(10, "unlink") +_S(11, "execve") +_S(12, "chdir") +_S(13, "time") +_S(14, "mknod") +_S(15, "chmod") +_S(16, "lchown") +_S(17, "break") +_S(18, "oldstat") +_S(19, "lseek") +_S(20, "getpid") +_S(21, "mount") +_S(22, "umount") +_S(23, "setuid") +_S(24, "getuid") +_S(25, "stime") +_S(26, "ptrace") +_S(27, "alarm") +_S(28, "oldfstat") +_S(29, "pause") +_S(30, "utime") +_S(31, "stty") +_S(32, "gtty") +_S(33, "access") +_S(34, "nice") +_S(35, "ftime") +_S(36, "sync") +_S(37, "kill") +_S(38, "rename") +_S(39, "mkdir") +_S(40, "rmdir") +_S(41, "dup") +_S(42, "pipe") +_S(43, "times") +_S(44, "prof") +_S(45, "brk") +_S(46, "setgid") +_S(47, "getgid") +_S(48, "signal") +_S(49, "geteuid") +_S(50, "getegid") +_S(51, "acct") +_S(52, "umount2") +_S(53, "lock") +_S(54, "ioctl") +_S(55, "fcntl") +_S(56, "mpx") +_S(57, "setpgid") +_S(58, "ulimit") +_S(59, "oldolduname") +_S(60, "umask") +_S(61, "chroot") +_S(62, "ustat") +_S(63, "dup2") +_S(64, "getppid") +_S(65, "getpgrp") +_S(66, "setsid") +_S(67, "sigaction") +_S(68, "sgetmask") +_S(69, "ssetmask") +_S(70, "setreuid") +_S(71, "setregid") +_S(72, "sigsuspend") +_S(73, "sigpending") +_S(74, "sethostname") +_S(75, "setrlimit") +_S(76, "getrlimit") +_S(77, "getrusage") +_S(78, "gettimeofday") +_S(79, "settimeofday") +_S(80, "getgroups") +_S(81, "setgroups") +_S(82, "select") +_S(83, "symlink") +_S(84, "oldlstat") +_S(85, "readlink") +_S(86, "uselib") +_S(87, "swapon") +_S(88, "reboot") +_S(89, "readdir") +_S(90, "mmap") +_S(91, "munmap") +_S(92, "truncate") +_S(93, "ftruncate") +_S(94, "fchmod") +_S(95, "fchown") +_S(96, "getpriority") +_S(97, "setpriority") +_S(98, "profil") +_S(99, "statfs") +_S(100, "fstatfs") +_S(101, "ioperm") +_S(102, "socketcall") +_S(103, "syslog") +_S(104, "setitimer") +_S(105, "getitimer") +_S(106, "stat") +_S(107, "lstat") +_S(108, "fstat") +_S(109, "olduname") +_S(110, "iopl") +_S(111, "vhangup") +_S(112, "idle") +_S(113, "vm86old") +_S(114, "wait4") +_S(115, "swapoff") +_S(116, "sysinfo") +_S(117, "ipc") +_S(118, "fsync") +_S(119, "sigreturn") +_S(120, "clone") +_S(121, "setdomainname") +_S(122, "uname") +_S(123, "modify_ldt") +_S(124, "adjtimex") +_S(125, "mprotect") +_S(126, "sigprocmask") +_S(127, "create_module") +_S(128, "init_module") +_S(129, "delete_module") +_S(130, "get_kernel_syms") +_S(131, "quotactl") +_S(132, "getpgid") +_S(133, "fchdir") +_S(134, "bdflush") +_S(135, "sysfs") +_S(136, "personality") +_S(137, "afs_syscall") +_S(138, "setfsuid") +_S(139, "setfsgid") +_S(140, "_llseek") +_S(141, "getdents") +_S(142, "_newselect") +_S(143, "flock") +_S(144, "msync") +_S(145, "readv") +_S(146, "writev") +_S(147, "getsid") +_S(148, "fdatasync") +_S(149, "_sysctl") +_S(150, "mlock") +_S(151, "munlock") +_S(152, "mlockall") +_S(153, "munlockall") +_S(154, "sched_setparam") +_S(155, "sched_getparam") +_S(156, "sched_setscheduler") +_S(157, "sched_getscheduler") +_S(158, "sched_yield") +_S(159, "sched_get_priority_max") +_S(160, "sched_get_priority_min") +_S(161, "sched_rr_get_interval") +_S(162, "nanosleep") +_S(163, "mremap") +_S(164, "setresuid") +_S(165, "getresuid") +_S(166, "vm86") +_S(167, "query_module") +_S(168, "poll") +_S(169, "nfsservctl") +_S(170, "setresgid") +_S(171, "getresgid") +_S(172, "prctl") +_S(173, "rt_sigreturn") +_S(174, "rt_sigaction") +_S(175, "rt_sigprocmask") +_S(176, "rt_sigpending") +_S(177, "rt_sigtimedwait") +_S(178, "rt_sigqueueinfo") +_S(179, "rt_sigsuspend") +_S(180, "pread64") +_S(181, "pwrite64") +_S(182, "chown") +_S(183, "getcwd") +_S(184, "capget") +_S(185, "capset") +_S(186, "sigaltstack") +_S(187, "sendfile") +_S(188, "getpmsg") +_S(189, "putpmsg") +_S(190, "vfork") +_S(191, "ugetrlimit") +_S(192, "mmap2") +_S(193, "truncate64") +_S(194, "ftruncate64") +_S(195, "stat64") +_S(196, "lstat64") +_S(197, "fstat64") +_S(198, "lchown32") +_S(199, "getuid32") +_S(200, "getgid32") +_S(201, "geteuid32") +_S(202, "getegid32") +_S(203, "setreuid32") +_S(204, "setregid32") +_S(205, "getgroups32") +_S(206, "setgroups32") +_S(207, "fchown32") +_S(208, "setresuid32") +_S(209, "getresuid32") +_S(210, "setresgid32") +_S(211, "getresgid32") +_S(212, "chown32") +_S(213, "setuid32") +_S(214, "setgid32") +_S(215, "setfsuid32") +_S(216, "setfsgid32") +_S(217, "pivot_root") +_S(218, "mincore") +_S(219, "madvise") +_S(219, "madvise1") +_S(220, "getdents64") +_S(221, "fcntl64") +_S(224, "gettid") +_S(225, "readahead") +_S(226, "setxattr") +_S(227, "lsetxattr") +_S(228, "fsetxattr") +_S(229, "getxattr") +_S(230, "lgetxattr") +_S(231, "fgetxattr") +_S(232, "listxattr") +_S(233, "llistxattr") +_S(234, "flistxattr") +_S(235, "removexattr") +_S(236, "lremovexattr") +_S(237, "fremovexattr") +_S(238, "tkill") +_S(239, "sendfile64") +_S(240, "futex") +_S(241, "sched_setaffinity") +_S(242, "sched_getaffinity") +_S(243, "set_thread_area") +_S(244, "get_thread_area") +_S(245, "io_setup") +_S(246, "io_destroy") +_S(247, "io_getevents") +_S(248, "io_submit") +_S(249, "io_cancel") +_S(250, "fadvise64") +_S(252, "exit_group") +_S(253, "lookup_dcookie") +_S(254, "epoll_create") +_S(255, "epoll_ctl") +_S(256, "epoll_wait") +_S(257, "remap_file_pages") +_S(258, "set_tid_address") +_S(259, "timer_create") +_S(260, "timer_settime") +_S(261, "timer_gettime") +_S(262, "timer_getoverrun") +_S(263, "timer_delete") +_S(264, "clock_settime") +_S(265, "clock_gettime") +_S(266, "clock_getres") +_S(267, "clock_nanosleep") +_S(268, "statfs64") +_S(269, "fstatfs64") +_S(270, "tgkill") +_S(271, "utimes") +_S(272, "fadvise64_64") +_S(273, "vserver") +_S(274, "mbind") +_S(275, "get_mempolicy") +_S(276, "set_mempolicy") +_S(277, "mq_open") +_S(278, "mq_unlink") +_S(279, "mq_timedsend") +_S(280, "mq_timedreceive") +_S(281, "mq_notify") +_S(282, "mq_getsetattr") +_S(283, "sys_kexec_load") +_S(284, "waitid") +// 285 is setaltroot but it is not defined (yet) +_S(286, "add_key") +_S(287, "request_key") +_S(288, "keyctl") +_S(289, "ioprio_set") +_S(290, "ioprio_get") +_S(291, "inotify_init") +_S(292, "inotify_add_watch") +_S(293, "inotify_rm_watch") +_S(294, "migrate_pages") +_S(295, "openat") +_S(296, "mkdirat") +_S(297, "mknodat") +_S(298, "fchownat") +_S(299, "futimesat") +_S(300, "fstatat64") +_S(301, "unlinkat") +_S(302, "renameat") +_S(303, "linkat") +_S(304, "symlinkat") +_S(305, "readlinkat") +_S(306, "fchmodat") +_S(307, "faccessat") +_S(308, "pselect6") +_S(309, "ppoll") +_S(310, "unshare") +_S(311, "set_robust_list") +_S(312, "get_robust_list") +_S(313, "splice") +_S(314, "sync_file_range") +_S(315, "tee") +_S(316, "vmsplice") +_S(317, "move_pages") +_S(318, "getcpu") +_S(319, "epoll_pwait") +_S(320, "utimensat") +_S(321, "signalfd") +_S(322, "timerfd") +_S(323, "eventfd") +_S(324, "fallocate") +_S(325, "timerfd_settime") +_S(326, "timerfd_gettime") +_S(327, "signalfd4") +_S(328, "eventfd2") +_S(329, "epoll_create1") +_S(330, "dup3") +_S(331, "pipe2") +_S(332, "inotify_init1") +_S(333, "preadv") +_S(334, "pwritev") +_S(335, "rt_tgsigqueueinfo") +_S(336, "perf_event_open") +_S(337, "recvmmsg") +_S(338, "fanotify_init") +_S(339, "fanotify_mark") +_S(340, "prlimit64") +_S(341, "name_to_handle_at") +_S(342, "open_by_handle_at") +_S(343, "clock_adjtime") +_S(344, "syncfs") +_S(345, "sendmmsg") +_S(346, "setns") +_S(347, "process_vm_readv") +_S(348, "process_vm_writev") +_S(349, "kcmp") +_S(350, "finit_module") +_S(351, "sched_setattr") +_S(352, "sched_getattr") +_S(353, "renameat2") +_S(354, "seccomp") +_S(355, "getrandom") +_S(356, "memfd_create") +_S(357, "bpf") +_S(358, "execveat") diff --git a/framework/src/audit/lib/ia64_table.h b/framework/src/audit/lib/ia64_table.h new file mode 100644 index 00000000..4a154670 --- /dev/null +++ b/framework/src/audit/lib/ia64_table.h @@ -0,0 +1,335 @@ +/* ia64_table.h -- + * Copyright 2005-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(1024, "ni_syscall") +_S(1025, "exit") +_S(1026, "read") +_S(1027, "write") +_S(1028, "open") +_S(1029, "close") +_S(1030, "creat") +_S(1031, "link") +_S(1032, "unlink") +_S(1033, "execve") +_S(1034, "chdir") +_S(1035, "fchdir") +_S(1036, "utimes") +_S(1037, "mknod") +_S(1038, "chmod") +_S(1039, "chown") +_S(1040, "lseek") +_S(1041, "getpid") +_S(1042, "getppid") +_S(1043, "mount") +_S(1044, "umount") +_S(1045, "setuid") +_S(1046, "getuid") +_S(1047, "geteuid") +_S(1048, "ptrace") +_S(1049, "access") +_S(1050, "sync") +_S(1051, "fsync") +_S(1052, "fdatasync") +_S(1053, "kill") +_S(1054, "rename") +_S(1055, "mkdir") +_S(1056, "rmdir") +_S(1057, "dup") +_S(1058, "pipe") +_S(1059, "times") +_S(1060, "brk") +_S(1061, "setgid") +_S(1062, "getgid") +_S(1063, "getegid") +_S(1064, "acct") +_S(1065, "ioctl") +_S(1066, "fcntl") +_S(1067, "umask") +_S(1068, "chroot") +_S(1069, "ustat") +_S(1070, "dup2") +_S(1071, "setreuid") +_S(1072, "setregid") +_S(1073, "getresuid") +_S(1074, "setresuid") +_S(1075, "getresgid") +_S(1076, "setresgid") +_S(1077, "getgroups") +_S(1078, "setgroups") +_S(1079, "getpgid") +_S(1080, "setpgid") +_S(1081, "setsid") +_S(1082, "getsid") +_S(1083, "sethostname") +_S(1084, "setrlimit") +_S(1085, "getrlimit") +_S(1086, "getrusage") +_S(1087, "gettimeofday") +_S(1088, "settimeofday") +_S(1089, "select") +_S(1090, "poll") +_S(1091, "symlink") +_S(1092, "readlink") +_S(1093, "uselib") +_S(1094, "swapon") +_S(1095, "swapoff") +_S(1096, "reboot") +_S(1097, "truncate") +_S(1098, "ftruncate") +_S(1099, "fchmod") +_S(1100, "fchown") +_S(1101, "getpriority") +_S(1102, "setpriority") +_S(1103, "statfs") +_S(1104, "fstatfs") +_S(1105, "gettid") +_S(1106, "semget") +_S(1107, "semop") +_S(1108, "semctl") +_S(1109, "msgget") +_S(1110, "msgsnd") +_S(1111, "msgrcv") +_S(1112, "msgctl") +_S(1113, "shmget") +_S(1114, "shmat") +_S(1115, "shmdt") +_S(1116, "shmctl") +_S(1117, "syslog") +_S(1118, "setitimer") +_S(1119, "getitimer") +_S(1120, "tux") +_S(1123, "vhangup") +_S(1124, "lchown") +_S(1125, "remap_file_pages") +_S(1126, "wait4") +_S(1127, "sysinfo") +_S(1128, "clone") +_S(1129, "setdomainname") +_S(1130, "uname") +_S(1131, "adjtimex") +_S(1133, "init_module") +_S(1134, "delete_module") +_S(1137, "quotactl") +_S(1138, "bdflush") +_S(1139, "sysfs") +_S(1140, "personality") +_S(1141, "afs_syscall") +_S(1142, "setfsuid") +_S(1143, "setfsgid") +_S(1144, "getdents") +_S(1145, "flock") +_S(1146, "readv") +_S(1147, "writev") +_S(1148, "pread64") +_S(1149, "pwrite64") +_S(1150, "_sysctl") +_S(1151, "mmap") +_S(1152, "munmap") +_S(1153, "mlock") +_S(1154, "mlockall") +_S(1155, "mprotect") +_S(1156, "mremap") +_S(1157, "msync") +_S(1158, "munlock") +_S(1159, "munlockall") +_S(1160, "sched_getparam") +_S(1161, "sched_setparam") +_S(1162, "sched_getscheduler") +_S(1163, "sched_setscheduler") +_S(1164, "sched_yield") +_S(1165, "sched_get_priority_max") +_S(1166, "sched_get_priority_min") +_S(1167, "sched_rr_get_interval") +_S(1168, "nanosleep") +_S(1169, "nfsservctl") +_S(1170, "prctl") +_S(1172, "mmap2") +_S(1173, "pciconfig_read") +_S(1174, "pciconfig_write") +_S(1175, "perfmonctl") +_S(1176, "sigaltstack") +_S(1177, "rt_sigaction") +_S(1178, "rt_sigpending") +_S(1179, "rt_sigprocmask") +_S(1180, "rt_sigqueueinfo") +_S(1181, "rt_sigreturn") +_S(1182, "rt_sigsuspend") +_S(1183, "rt_sigtimedwait") +_S(1184, "getcwd") +_S(1185, "capget") +_S(1186, "capset") +_S(1187, "sendfile") +_S(1188, "getpmsg") +_S(1189, "putpmsg") +_S(1190, "socket") +_S(1191, "bind") +_S(1192, "connect") +_S(1193, "listen") +_S(1194, "accept") +_S(1195, "getsockname") +_S(1196, "getpeername") +_S(1197, "socketpair") +_S(1198, "send") +_S(1199, "sendto") +_S(1200, "recv") +_S(1201, "recvfrom") +_S(1202, "shutdown") +_S(1203, "setsockopt") +_S(1204, "getsockopt") +_S(1205, "sendmsg") +_S(1206, "recvmsg") +_S(1207, "pivot_root") +_S(1208, "mincore") +_S(1209, "madvise") +_S(1210, "stat") +_S(1211, "lstat") +_S(1212, "fstat") +_S(1213, "clone2") +_S(1214, "getdents64") +_S(1215, "getunwind") +_S(1216, "readahead") +_S(1217, "setxattr") +_S(1218, "lsetxattr") +_S(1219, "fsetxattr") +_S(1220, "getxattr") +_S(1221, "lgetxattr") +_S(1222, "fgetxattr") +_S(1223, "listxattr") +_S(1224, "llistxattr") +_S(1225, "flistxattr") +_S(1226, "removexattr") +_S(1227, "lremovexattr") +_S(1228, "fremovexattr") +_S(1229, "tkill") +_S(1230, "futex") +_S(1231, "sched_setaffinity") +_S(1232, "sched_getaffinity") +_S(1233, "set_tid_address") +_S(1234, "fadvise64") +_S(1235, "tgkill") +_S(1236, "exit_group") +_S(1237, "lookup_dcookie") +_S(1238, "io_setup") +_S(1239, "io_destroy") +_S(1240, "io_getevents") +_S(1241, "io_submit") +_S(1242, "io_cancel") +_S(1243, "epoll_create") +_S(1244, "epoll_ctl") +_S(1245, "epoll_wait") +_S(1246, "restart_syscall") +_S(1247, "semtimedop") +_S(1248, "timer_create") +_S(1249, "timer_settime") +_S(1250, "timer_gettime") +_S(1251, "timer_getoverrun") +_S(1252, "timer_delete") +_S(1253, "clock_settime") +_S(1254, "clock_gettime") +_S(1255, "clock_getres") +_S(1256, "clock_nanosleep") +_S(1257, "fstatfs64") +_S(1258, "statfs64") +_S(1259, "mbind") +_S(1260, "get_mempolicy") +_S(1261, "set_mempolicy") +_S(1262, "mq_open") +_S(1263, "mq_unlink") +_S(1264, "mq_timedsend") +_S(1265, "mq_timedreceive") +_S(1266, "mq_notify") +_S(1267, "mq_getsetattr") +_S(1268, "kexec_load") +_S(1269, "vserver") +_S(1270, "waitid") +_S(1271, "add_key") +_S(1272, "request_key") +_S(1273, "keyctl") +_S(1274, "ioprio_set") +_S(1275, "ioprio_get") +_S(1276, "set_zone_reclaim") +_S(1277, "inotify_init") +_S(1278, "inotify_add_watch") +_S(1279, "inotify_rm_watch") +_S(1280, "migrate_pages") +_S(1281, "openat") +_S(1282, "mkdirat") +_S(1283, "mknodat") +_S(1284, "fchownat") +_S(1285, "futimesat") +_S(1286, "newfstatat") +_S(1287, "unlinkat") +_S(1288, "renameat") +_S(1289, "linkat") +_S(1290, "symlinkat") +_S(1291, "readlinkat") +_S(1292, "fchmodat") +_S(1293, "faccessat") +_S(1294, "pselect") +_S(1295, "ppoll") +_S(1296, "unshare") +_S(1297, "splice") +_S(1298, "set_robust_list") +_S(1299, "get_robust_list") +_S(1300, "sync_file_range") +_S(1301, "tee") +_S(1302, "vmsplice") +_S(1303, "fallocate") +_S(1304, "getcpu") +_S(1305, "epoll_pwait") +_S(1306, "utimensat") +_S(1307, "signalfd") +_S(1308, "timerfd") +_S(1309, "eventfd") +_S(1310, "timerfd_create") +_S(1311, "timerfd_settime") +_S(1312, "timerfd_gettime") +_S(1313, "signalfd4") +_S(1314, "eventfd2") +_S(1315, "epoll_create1") +_S(1316, "dup3") +_S(1317, "pipe2") +_S(1318, "inotify_init1") +_S(1319, "preadv") +_S(1320, "pwritev") +_S(1321, "rt_tgsigqueueinfo") +_S(1322, "recvmmsg") +_S(1323, "fanotify_init") +_S(1324, "fanotify_mark") +_S(1325, "prlimit64") +_S(1326, "name_to_handle_at") +_S(1327, "open_by_handle_at") +_S(1328, "clock_adjtime") +_S(1329, "syncfs") +_S(1330, "setns") +_S(1331, "sendmmsg") +_S(1332, "process_vm_readv") +_S(1333, "process_vm_writev") +_S(1334, "accept4") +_S(1335, "finit_module") +_S(1336, "sched_setattr") +_S(1337, "sched_getattr") +_S(1338, "renameat2") +_S(1339, "getrandom") +_S(1340, "memfd_create") +_S(1341, "bpf") +_S(1342, "execveat") diff --git a/framework/src/audit/lib/libaudit.c b/framework/src/audit/lib/libaudit.c new file mode 100644 index 00000000..6311b813 --- /dev/null +++ b/framework/src/audit/lib/libaudit.c @@ -0,0 +1,1606 @@ +/* libaudit.c -- + * Copyright 2004-2009,2012,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Rickard E. (Rik) Faith + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* O_NOFOLLOW needs gnu defined */ +#include /* for PATH_MAX */ +#include +#include + +#include "libaudit.h" +#include "private.h" +#include "errormsg.h" + +/* #defines for the audit failure query */ +#define CONFIG_FILE "/etc/libaudit.conf" + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(const char *, int); +}; + +struct nv_list +{ + const char *name; + int option; +}; + +struct libaudit_conf +{ + auditfail_t failure_action; +}; + +static const struct nv_list failure_actions[] = +{ + {"ignore", FAIL_IGNORE }, + {"log", FAIL_LOG }, + {"terminate", FAIL_TERMINATE }, + { NULL, 0 } +}; + +int _audit_permadded = 0; +int _audit_archadded = 0; +int _audit_syscalladded = 0; +unsigned int _audit_elf = 0U; +static struct libaudit_conf config; + +static int audit_failure_parser(const char *val, int line); +static int audit_name_to_uid(const char *name, uid_t *uid); +static int audit_name_to_gid(const char *name, gid_t *gid); + +static const struct kw_pair keywords[] = +{ + {"failure_action", audit_failure_parser }, + { NULL, NULL } +}; + +static int audit_priority(int xerrno) +{ + /* If they've compiled their own kernel and did not include + * the audit susbsystem, they will get ECONNREFUSED. We'll + * demote the message to debug so its not lost entirely. */ + if (xerrno == ECONNREFUSED) + return LOG_DEBUG; + else + return LOG_WARNING; +} + +int audit_request_status(int fd) +{ + int rc = audit_send(fd, AUDIT_GET, NULL, 0); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending status request (%s)", strerror(-rc)); + return rc; +} +hidden_def(audit_request_status) + +/* + * Set everything to its default value + */ +static void clear_config(void) +{ + config.failure_action = FAIL_IGNORE; +} + +/* Get 1 line from file */ +static char *get_line(FILE *f, char *buf, size_t len) +{ + if (fgets(buf, len, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) + *ptr = 0; + return buf; + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr, *saved=NULL; + + nv->name = NULL; + nv->value = NULL; + ptr = audit_strsplit_r(buf, &saved); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = audit_strsplit_r(NULL, &saved); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = audit_strsplit_r(NULL, &saved); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* Make sure there's nothing else */ + ptr = audit_strsplit_r(NULL, &saved); + if (ptr) + return 1; + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int audit_failure_parser(const char *val, int line) +{ + int i; + + audit_msg(LOG_DEBUG, "audit_failure_parser called with: %s", val); + for (i=0; failure_actions[i].name != NULL; i++) { + if (strcasecmp(val, failure_actions[i].name) == 0) { + config.failure_action = failure_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", val, line); + return 1; +} + +/* + * Read the /etc/libaudit.conf file and all tunables. + */ +static int load_libaudit_config(const char *path) +{ + int fd, rc, lineno = 1; + struct stat st; + FILE *f; + char buf[128]; + + /* open the file */ + rc = open(path, O_NOFOLLOW|O_RDONLY); + if (rc < 0) { + if (errno != ENOENT) { + audit_msg(LOG_ERR, "Error opening %s (%s)", + path, strerror(errno)); + return 1; + } + audit_msg(LOG_WARNING, + "Config file %s doesn't exist, skipping", path); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + audit_msg(LOG_DEBUG, "Config file %s opened for parsing", path); + if (fstat(fd, &st) < 0) { + audit_msg(LOG_ERR, "Error fstat'ing %s (%s)", + path, strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + audit_msg(LOG_ERR, "Error - %s isn't owned by root", path); + close(fd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + audit_msg(LOG_ERR, "Error - %s is world writable", path); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + audit_msg(LOG_ERR, "Error - %s is not a regular file", path); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + audit_msg(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf, sizeof(buf))) { + // convert line into name-value pair + const struct kw_pair *kw; + struct nv_pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + audit_msg(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, path); + break; + case 2: // no '=' sign + audit_msg(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, path); + break; + default: // something else went wrong... + audit_msg(LOG_ERR, + "Unknown error for line %d in %s", + lineno, path); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + audit_msg(LOG_ERR, + "Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, path); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(nv.value, lineno); + if (rc != 0) { + fclose(f); + return 1; // local parser puts message out + } + + lineno++; + } + + fclose(f); + return 0; +} + + +/* + * This function is called to get the value of the failure_action + * tunable stored in /etc/libaudit.conf. The function returns 1 if + * the tunable is not found or there is an error. If the tunable is found, + * 0 is returned the the tunable value is saved in the failmode parameter. + */ +int get_auditfail_action(auditfail_t *failmode) +{ + clear_config(); + + if (load_libaudit_config(CONFIG_FILE)) { + *failmode = config.failure_action; + return 1; + } + + *failmode = config.failure_action; + return 0; +} + +int audit_set_enabled(int fd, uint32_t enabled) +{ + int rc; + struct audit_status s; + + memset(&s, 0, sizeof(s)); + s.mask = AUDIT_STATUS_ENABLED; + s.enabled = enabled; + rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending enable request (%s)", strerror(-rc)); + return rc; +} + +/* + * This function will return 0 if auditing is NOT enabled and + * 1 if enabled, and -1 on error. + */ +int audit_is_enabled(int fd) +{ + int rc; + + if (fd < 0) + return 0; + + if ((rc = audit_request_status(fd)) > 0) { + struct audit_reply rep; + int i; + int timeout = 40; /* tenths of seconds */ + struct pollfd pfd[1]; + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + + for (i = 0; i < timeout; i++) { + do { + rc = poll(pfd, 1, 100); + } while (rc < 0 && errno == EINTR); + + rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING,0); + if (rc > 0) { + /* If we get done or error, break out */ + if (rep.type == NLMSG_DONE || + rep.type == NLMSG_ERROR) + break; + + /* If its not status, keep looping */ + if (rep.type != AUDIT_GET) + continue; + + /* Found it... */ + return rep.status->enabled; + } + } + } + if (rc == -ECONNREFUSED) { + /* This is here to let people that build their own kernel + and disable the audit system get in. ECONNREFUSED is + issued by the kernel when there is "no on listening". */ + return 0; + } else if (rc == -EPERM && getuid() != 0) { + /* If we get this, then the kernel supports auditing + * but we don't have enough privilege to write to the + * socket. Therefore, we have already been authenticated + * and we are a common user. Just act as though auditing + * is not enabled. Any other error we take seriously. + * This is here basically to satisfy Xscreensaver. */ + return 0; + } + return -1; +} + +int audit_set_failure(int fd, uint32_t failure) +{ + int rc; + struct audit_status s; + + memset(&s, 0, sizeof(s)); + s.mask = AUDIT_STATUS_FAILURE; + s.failure = failure; + rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending failure mode request (%s)", + strerror(-rc)); + return rc; +} + +/* + * This function returns -1 on error and 1 on success. + */ +int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode) +{ + struct audit_status s; + struct audit_reply rep; + struct pollfd pfd[1]; + int rc; + + memset(&s, 0, sizeof(s)); + s.mask = AUDIT_STATUS_PID; + s.pid = pid; + rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); + if (rc < 0) { + audit_msg(audit_priority(errno), + "Error setting audit daemon pid (%s)", + strerror(-rc)); + return rc; + } + if (wmode == WAIT_NO) + return 1; + + /* Now we'll see if there's any reply message. This only + happens on error. It is not fatal if there is no message. + As a matter of fact, we don't do anything with the message + besides gobble it. */ + pfd[0].fd = fd; + pfd[0].events = POLLIN; + do { + rc = poll(pfd, 1, 100); /* .1 second */ + } while (rc < 0 && errno == EINTR); + + (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + return 1; +} + +int audit_set_rate_limit(int fd, uint32_t limit) +{ + int rc; + struct audit_status s; + + memset(&s, 0, sizeof(s)); + s.mask = AUDIT_STATUS_RATE_LIMIT; + s.rate_limit = limit; + rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending rate limit request (%s)", + strerror(-rc)); + return rc; +} + +int audit_set_backlog_limit(int fd, uint32_t limit) +{ + int rc; + struct audit_status s; + + memset(&s, 0, sizeof(s)); + s.mask = AUDIT_STATUS_BACKLOG_LIMIT; + s.backlog_limit = limit; + rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending backlog limit request (%s)", + strerror(-rc)); + return rc; +} + +int audit_set_backlog_wait_time(int fd, uint32_t bwt) +{ + int rc = -1; +#if HAVE_DECL_AUDIT_VERSION_BACKLOG_WAIT_TIME + struct audit_status s; + + memset(&s, 0, sizeof(s)); + s.mask = AUDIT_STATUS_BACKLOG_WAIT_TIME; + s.backlog_wait_time = bwt; + rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending backlog limit request (%s)", + strerror(-rc)); +#endif + return rc; +} + +int audit_set_feature(int fd, unsigned feature, unsigned value, unsigned lock) +{ +#if HAVE_DECL_AUDIT_FEATURE_VERSION + int rc; + struct audit_features f; + + memset(&f, 0, sizeof(f)); + f.mask = AUDIT_FEATURE_TO_MASK(feature); + if (value) + f.features = AUDIT_FEATURE_TO_MASK(feature); + if (lock) + f.lock = AUDIT_FEATURE_TO_MASK(feature); + rc = audit_send(fd, AUDIT_SET_FEATURE, &f, sizeof(f)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error setting feature (%s)", + strerror(-rc)); + return rc; +#else + errno = EINVAL; + return -1; +#endif +} +hidden_def(audit_set_feature) + +int audit_request_features(int fd) +{ +#if HAVE_DECL_AUDIT_FEATURE_VERSION + int rc; + struct audit_features f; + + memset(&f, 0, sizeof(f)); + rc = audit_send(fd, AUDIT_GET_FEATURE, &f, sizeof(f)); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error getting feature (%s)", + strerror(-rc)); + return rc; +#else + errno = EINVAL; + return -1; +#endif +} +hidden_def(audit_request_features) + +extern int audit_set_loginuid_immutable(int fd) +{ +#if HAVE_DECL_AUDIT_FEATURE_VERSION + return audit_set_feature(fd, AUDIT_FEATURE_LOGINUID_IMMUTABLE, 1, 1); +#else + errno = EINVAL; + return -1; +#endif +} + +int audit_request_rules_list_data(int fd) +{ + int rc = audit_send(fd, AUDIT_LIST_RULES, NULL, 0); + if (rc < 0 && rc != -EINVAL) + audit_msg(audit_priority(errno), + "Error sending rule list data request (%s)", + strerror(-rc)); + return rc; +} + +int audit_request_signal_info(int fd) +{ + int rc = audit_send(fd, AUDIT_SIGNAL_INFO, NULL, 0); + if (rc < 0) + audit_msg(LOG_WARNING, + "Error sending signal_info request (%s)", + strerror(-rc)); + return rc; +} + +int audit_update_watch_perms(struct audit_rule_data *rule, int perms) +{ + int i, done=0; + + if (rule->field_count < 1) + return -1; + + // First see if we have an entry we are updating + for (i=0; i< rule->field_count; i++) { + if (rule->fields[i] == AUDIT_PERM) { + rule->values[i] = perms; + done = 1; + } + } + if (!done) { + // If not check to see if we have room to add a field + if (rule->field_count >= (AUDIT_MAX_FIELDS - 1)) + return -2; + + // Add the perm + rule->fields[rule->field_count] = AUDIT_PERM; + rule->fieldflags[rule->field_count] = AUDIT_EQUAL; + rule->values[rule->field_count] = perms; + rule->field_count++; + } + return 0; +} + +int audit_add_watch(struct audit_rule_data **rulep, const char *path) +{ + return audit_add_watch_dir(AUDIT_WATCH, rulep, path); +} + +int audit_add_dir(struct audit_rule_data **rulep, const char *path) +{ + return audit_add_watch_dir(AUDIT_DIR, rulep, path); +} + +int audit_add_watch_dir(int type, struct audit_rule_data **rulep, + const char *path) +{ + size_t len = strlen(path); + struct audit_rule_data *rule = *rulep; + + if (rule && rule->field_count) { + audit_msg(LOG_ERR, "Rule is not empty\n"); + return -1; + } + if (type != AUDIT_WATCH && type != AUDIT_DIR) { + audit_msg(LOG_ERR, "Invalid type used\n"); + return -1; + } + + *rulep = realloc(rule, len + sizeof(*rule)); + if (*rulep == NULL) { + free(rule); + audit_msg(LOG_ERR, "Cannot realloc memory!\n"); + return -1; + } + rule = *rulep; + memset(rule, 0, len + sizeof(*rule)); + + rule->flags = AUDIT_FILTER_EXIT; + rule->action = AUDIT_ALWAYS; + audit_rule_syscallbyname_data(rule, "all"); + rule->field_count = 2; + rule->fields[0] = type; + rule->values[0] = len; + rule->fieldflags[0] = AUDIT_EQUAL; + rule->buflen = len; + memcpy(&rule->buf[0], path, len); + + // Default to all permissions + rule->fields[1] = AUDIT_PERM; + rule->fieldflags[1] = AUDIT_EQUAL; + rule->values[1] = AUDIT_PERM_READ | AUDIT_PERM_WRITE | + AUDIT_PERM_EXEC | AUDIT_PERM_ATTR; + + _audit_permadded = 1; + + return 0; +} +hidden_def(audit_add_watch_dir) + +int audit_add_rule_data(int fd, struct audit_rule_data *rule, + int flags, int action) +{ + int rc; + + if (flags == AUDIT_FILTER_ENTRY) { + audit_msg(LOG_WARNING, "Use of entry filter is deprecated"); + return -2; + } + rule->flags = flags; + rule->action = action; + rc = audit_send(fd, AUDIT_ADD_RULE, rule, + sizeof(struct audit_rule_data) + rule->buflen); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending add rule data request (%s)", + errno == EEXIST ? + "Rule exists" : strerror(-rc)); + return rc; +} + +int audit_delete_rule_data(int fd, struct audit_rule_data *rule, + int flags, int action) +{ + int rc; + + if (flags == AUDIT_FILTER_ENTRY) { + audit_msg(LOG_WARNING, "Use of entry filter is deprecated"); + return -2; + } + rule->flags = flags; + rule->action = action; + rc = audit_send(fd, AUDIT_DEL_RULE, rule, + sizeof(struct audit_rule_data) + rule->buflen); + if (rc < 0) { + if (rc == -ENOENT) + audit_msg(LOG_WARNING, + "Error sending delete rule request (No rule matches)"); + else + audit_msg(audit_priority(errno), + "Error sending delete rule data request (%s)", + strerror(-rc)); + } + return rc; +} + +/* + * This function is part of the directory auditing code + */ +int audit_trim_subtrees(int fd) +{ + int rc = audit_send(fd, AUDIT_TRIM, NULL, 0); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending trim subtrees command (%s)", + strerror(-rc)); + return rc; +} + +/* + * This function is part of the directory auditing code + */ +int audit_make_equivalent(int fd, const char *mount_point, + const char *subtree) +{ + int rc; + size_t len1 = strlen(mount_point); + size_t len2 = strlen(subtree); + struct { + uint32_t sizes[2]; + unsigned char buf[]; + } *cmd = malloc(sizeof(*cmd) + len1 + len2); + + memset(cmd, 0, sizeof(*cmd) + len1 + len2); + + cmd->sizes[0] = len1; + cmd->sizes[1] = len2; + memcpy(&cmd->buf[0], mount_point, len1); + memcpy(&cmd->buf[len1], subtree, len2); + + rc = audit_send(fd, AUDIT_MAKE_EQUIV, cmd, sizeof(*cmd) + len1 + len2); + if (rc < 0) + audit_msg(audit_priority(errno), + "Error sending make_equivalent command (%s)", + strerror(-rc)); + free(cmd); + return rc; +} + +/* + * This function will retreive the loginuid or -1 if there + * is an error. + */ +uid_t audit_getloginuid(void) +{ + uid_t uid; + int len, in; + char buf[16]; + + errno = 0; + in = open("/proc/self/loginuid", O_NOFOLLOW|O_RDONLY); + if (in < 0) + return -1; + do { + len = read(in, buf, sizeof(buf)); + } while (len < 0 && errno == EINTR); + close(in); + if (len < 0 || len >= sizeof(buf)) + return -1; + buf[len] = 0; + errno = 0; + uid = strtol(buf, 0, 10); + if (errno) + return -1; + else + return uid; +} + +/* + * This function returns 0 on success and 1 on failure + */ +int audit_setloginuid(uid_t uid) +{ + char loginuid[16]; + int o, count, rc = 0; + + errno = 0; + count = snprintf(loginuid, sizeof(loginuid), "%u", uid); + o = open("/proc/self/loginuid", O_NOFOLLOW|O_WRONLY|O_TRUNC); + if (o >= 0) { + int block, offset = 0; + + while (count > 0) { + block = write(o, &loginuid[offset], (unsigned)count); + + if (block < 0) { + if (errno == EINTR) + continue; + audit_msg(LOG_ERR, "Error writing loginuid"); + close(o); + return 1; + } + offset += block; + count -= block; + } + close(o); + } else { + audit_msg(LOG_ERR, "Error opening /proc/self/loginuid"); + rc = 1; + } + return rc; +} + +int audit_rule_syscall_data(struct audit_rule_data *rule, int scall) +{ + int word = AUDIT_WORD(scall); + int bit = AUDIT_BIT(scall); + + if (word >= (AUDIT_BITMASK_SIZE-1)) + return -1; + rule->mask[word] |= bit; + return 0; +} +hidden_def(audit_rule_syscall_data) + +int audit_rule_syscallbyname_data(struct audit_rule_data *rule, + const char *scall) +{ + int nr, i; + int machine; + + if (!strcmp(scall, "all")) { + for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++) + rule->mask[i] = ~0; + return 0; + } + if (!_audit_elf) + machine = audit_detect_machine(); + else + machine = audit_elf_to_machine(_audit_elf); + if (machine < 0) + return -2; + nr = audit_name_to_syscall(scall, machine); + if (nr < 0) { + if (isdigit(scall[0])) + nr = strtol(scall, NULL, 0); + } + if (nr >= 0) + return audit_rule_syscall_data(rule, nr); + return -1; +} +hidden_def(audit_rule_syscallbyname_data) + +int audit_rule_interfield_comp_data(struct audit_rule_data **rulep, + const char *pair, + int flags) { + const char *f = pair; + char *v; + int op; + int field1, field2; + struct audit_rule_data *rule = *rulep; + + if (f == NULL) + return -1; + + if (rule->field_count >= (AUDIT_MAX_FIELDS - 1)) + return -28; + + /* look for 2-char operators first + then look for 1-char operators afterwards + when found, null out the bytes under the operators to split + and set value pointer just past operator bytes + */ + if ( (v = strstr(pair, "!=")) ) { + *v++ = '\0'; + *v++ = '\0'; + op = AUDIT_NOT_EQUAL; + } else if ( (v = strstr(pair, "=")) ) { + *v++ = '\0'; + op = AUDIT_EQUAL; + } else { + return -13; + } + + if (*f == 0) + return -24; + + if (*v == 0) + return -25; + + if ((field1 = audit_name_to_field(f)) < 0) + return -26; + + if ((field2 = audit_name_to_field(v)) < 0) + return -27; + + /* Interfield comparison can only be in exit filter */ + if (flags != AUDIT_FILTER_EXIT) + return -7; + + // It should always be AUDIT_FIELD_COMPARE + rule->fields[rule->field_count] = AUDIT_FIELD_COMPARE; + rule->fieldflags[rule->field_count] = op; + /* oh god, so many permutations */ + switch (field1) + { + /* UID comparison */ + case AUDIT_EUID: + switch(field2) { + case AUDIT_LOGINUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_EUID; + break; + case AUDIT_FSUID: + rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_FSUID; + break; + case AUDIT_OBJ_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_OBJ_UID; + break; + case AUDIT_SUID: + rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_SUID; + break; + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_EUID; + break; + default: + return -1; + } + break; + case AUDIT_FSUID: + switch(field2) { + case AUDIT_LOGINUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_FSUID; + break; + case AUDIT_EUID: + rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_FSUID; + break; + case AUDIT_OBJ_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_FSUID_TO_OBJ_UID; + break; + case AUDIT_SUID: + rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_FSUID; + break; + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_FSUID; + break; + default: + return -1; + } + break; + case AUDIT_LOGINUID: + switch(field2) { + case AUDIT_EUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_EUID; + break; + case AUDIT_FSUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_FSUID; + break; + case AUDIT_OBJ_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_OBJ_UID; + break; + case AUDIT_SUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_SUID; + break; + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_AUID; + break; + default: + return -1; + } + break; + case AUDIT_SUID: + switch(field2) { + case AUDIT_LOGINUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_SUID; + break; + case AUDIT_EUID: + rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_SUID; + break; + case AUDIT_FSUID: + rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_FSUID; + break; + case AUDIT_OBJ_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_OBJ_UID; + break; + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_SUID; + break; + default: + return -1; + } + break; + case AUDIT_OBJ_UID: + switch(field2) { + case AUDIT_LOGINUID: + rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_OBJ_UID; + break; + case AUDIT_EUID: + rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_OBJ_UID; + break; + case AUDIT_FSUID: + rule->values[rule->field_count] = AUDIT_COMPARE_FSUID_TO_OBJ_UID; + break; + case AUDIT_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_OBJ_UID; + break; + case AUDIT_SUID: + rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_OBJ_UID; + break; + default: + return -1; + } + break; + case AUDIT_UID: + switch(field2) { + case AUDIT_LOGINUID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_AUID; + break; + case AUDIT_EUID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_EUID; + break; + case AUDIT_FSUID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_FSUID; + break; + case AUDIT_OBJ_UID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_OBJ_UID; + break; + case AUDIT_SUID: + rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_SUID; + break; + default: + return -1; + } + break; + + /* GID comparisons */ + case AUDIT_EGID: + switch(field2) { + case AUDIT_FSGID: + rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_FSGID; + break; + case AUDIT_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_EGID; + break; + case AUDIT_OBJ_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_OBJ_GID; + break; + case AUDIT_SGID: + rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_SGID; + break; + default: + return -1; + } + break; + case AUDIT_FSGID: + switch(field2) { + case AUDIT_SGID: + rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_FSGID; + break; + case AUDIT_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_FSGID; + break; + case AUDIT_OBJ_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_FSGID_TO_OBJ_GID; + break; + case AUDIT_EGID: + rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_FSGID; + break; + default: + return -1; + } + break; + case AUDIT_GID: + switch(field2) { + case AUDIT_EGID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_EGID; + break; + case AUDIT_FSGID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_FSGID; + break; + case AUDIT_OBJ_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_OBJ_GID; + break; + case AUDIT_SGID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_SGID; + break; + default: + return -1; + } + break; + case AUDIT_OBJ_GID: + switch(field2) { + case AUDIT_EGID: + rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_OBJ_GID; + break; + case AUDIT_FSGID: + rule->values[rule->field_count] = AUDIT_COMPARE_FSGID_TO_OBJ_GID; + break; + case AUDIT_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_OBJ_GID; + break; + case AUDIT_SGID: + rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_OBJ_GID; + break; + default: + return -1; + } + break; + case AUDIT_SGID: + switch(field2) { + case AUDIT_FSGID: + rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_FSGID; + break; + case AUDIT_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_SGID; + break; + case AUDIT_OBJ_GID: + rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_OBJ_GID; + break; + case AUDIT_EGID: + rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_SGID; + break; + default: + return -1; + } + break; + default: + return -1; + break; + } + rule->field_count++; + return 0; +} + +int audit_determine_machine(const char *arch) +{ // What do we want? i686, x86_64, ia64 or b64, b32 + int machine; + unsigned int bits = 0; + + if (strcasecmp("b64", arch) == 0) { + bits = __AUDIT_ARCH_64BIT; + machine = audit_detect_machine(); + } else if (strcasecmp("b32", arch) == 0) { + bits = ~__AUDIT_ARCH_64BIT; + machine = audit_detect_machine(); + } else { + machine = audit_name_to_machine(arch); + if (machine < 0) { + // See if its numeric + unsigned int ival; + errno = 0; + ival = strtoul(arch, NULL, 16); + if (errno) + return -4; + machine = audit_elf_to_machine(ival); + } + } + + if (machine < 0) + return -4; + + /* Here's where we fixup the machine. For example, they give + * x86_64 & want 32 bits we translate that to i686. */ + if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_86_64) + machine = MACH_X86; + else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_PPC64) + machine = MACH_PPC; + else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_S390X) + machine = MACH_S390; + else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_AARCH64) + machine = MACH_ARM; + + /* Check for errors - return -6 + * We don't allow 32 bit machines to specify 64 bit. */ + switch (machine) + { + case MACH_X86: + if (bits == __AUDIT_ARCH_64BIT) + return -6; + break; + case MACH_IA64: + if (bits == ~__AUDIT_ARCH_64BIT) + return -6; + break; + case MACH_PPC: + if (bits == __AUDIT_ARCH_64BIT) + return -6; + break; + case MACH_S390: + if (bits == __AUDIT_ARCH_64BIT) + return -6; + break; +#ifdef WITH_ARM + case MACH_ARM: + if (bits == __AUDIT_ARCH_64BIT) + return -6; // Deadcode - just incase of mistake + break; +#endif +#ifdef WITH_AARCH64 + case MACH_AARCH64: + if (bits && bits != __AUDIT_ARCH_64BIT) + return -6; + break; +#endif + case MACH_86_64: /* fallthrough */ + case MACH_PPC64: /* fallthrough */ + case MACH_PPC64LE: /* fallthrough */ + case MACH_S390X: /* fallthrough */ + break; + default: + return -6; + } + return machine; +} + +int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair, + int flags) +{ + const char *f = pair; + char *v; + int op; + int field; + int vlen; + int offset; + struct audit_rule_data *rule = *rulep; + + if (f == NULL) + return -1; + + if (rule->field_count >= (AUDIT_MAX_FIELDS - 1)) + return -28; + + /* look for 2-char operators first + then look for 1-char operators afterwards + when found, null out the bytes under the operators to split + and set value pointer just past operator bytes + */ + if ( (v = strstr(pair, "!=")) ) { + *v++ = '\0'; + *v++ = '\0'; + op = AUDIT_NOT_EQUAL; + } else if ( (v = strstr(pair, ">=")) ) { + *v++ = '\0'; + *v++ = '\0'; + op = AUDIT_GREATER_THAN_OR_EQUAL; + } else if ( (v = strstr(pair, "<=")) ) { + *v++ = '\0'; + *v++ = '\0'; + op = AUDIT_LESS_THAN_OR_EQUAL; + } else if ( (v = strstr(pair, "&=")) ) { + *v++ = '\0'; + *v++ = '\0'; + op = AUDIT_BIT_TEST; + } else if ( (v = strstr(pair, "=")) ) { + *v++ = '\0'; + op = AUDIT_EQUAL; + } else if ( (v = strstr(pair, ">")) ) { + *v++ = '\0'; + op = AUDIT_GREATER_THAN; + } else if ( (v = strstr(pair, "<")) ) { + *v++ = '\0'; + op = AUDIT_LESS_THAN; + } else if ( (v = strstr(pair, "&")) ) { + *v++ = '\0'; + op = AUDIT_BIT_MASK; + } + + if (v == NULL) + return -1; + + if (*f == 0) + return -22; + + if (*v == 0) + return -20; + + if ((field = audit_name_to_field(f)) < 0) + return -2; + + /* Exclude filter can be used only with MSGTYPE field */ + if (flags == AUDIT_FILTER_EXCLUDE && field != AUDIT_MSGTYPE) + return -12; + + rule->fields[rule->field_count] = field; + rule->fieldflags[rule->field_count] = op; + switch (field) + { + case AUDIT_UID: + case AUDIT_EUID: + case AUDIT_SUID: + case AUDIT_FSUID: + case AUDIT_LOGINUID: + case AUDIT_OBJ_UID: + // Do positive & negative separate for 32 bit systems + vlen = strlen(v); + if (isdigit((char)*(v))) + rule->values[rule->field_count] = + strtoul(v, NULL, 0); + else if (vlen >= 2 && *(v)=='-' && + (isdigit((char)*(v+1)))) + rule->values[rule->field_count] = + strtol(v, NULL, 0); + else { + if (strcmp(v, "unset") == 0) + rule->values[rule->field_count] = + 4294967295; + else if (audit_name_to_uid(v, + &rule->values[rule->field_count])) { + audit_msg(LOG_ERR, "Unknown user: %s", + v); + return -2; + } + } + break; + case AUDIT_GID: + case AUDIT_EGID: + case AUDIT_SGID: + case AUDIT_FSGID: + case AUDIT_OBJ_GID: + if (isdigit((char)*(v))) + rule->values[rule->field_count] = + strtol(v, NULL, 0); + else { + if (audit_name_to_gid(v, + &rule->values[rule->field_count])) { + audit_msg(LOG_ERR, "Unknown group: %s", + v); + return -2; + } + } + break; + case AUDIT_EXIT: + if (flags != AUDIT_FILTER_EXIT) + return -7; + vlen = strlen(v); + if (isdigit((char)*(v))) + rule->values[rule->field_count] = + strtol(v, NULL, 0); + else if (vlen >= 2 && *(v)=='-' && + (isdigit((char)*(v+1)))) + rule->values[rule->field_count] = + strtol(v, NULL, 0); + else { + rule->values[rule->field_count] = + audit_name_to_errno(v); + if (rule->values[rule->field_count] == 0) + return -15; + } + break; + case AUDIT_MSGTYPE: + if (flags != AUDIT_FILTER_EXCLUDE && + flags != AUDIT_FILTER_USER) + return -9; + + if (isdigit((char)*(v))) + rule->values[rule->field_count] = + strtol(v, NULL, 0); + else + if (audit_name_to_msg_type(v) > 0) + rule->values[rule->field_count] = + audit_name_to_msg_type(v); + else + return -8; + break; + /* These next few are strings */ + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + case AUDIT_WATCH: + case AUDIT_DIR: + /* Watch & object filtering is invalid on anything + * but exit */ + if (flags != AUDIT_FILTER_EXIT) + return -7; + if (field == AUDIT_WATCH || field == AUDIT_DIR) + _audit_permadded = 1; + + /* fallthrough */ + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_FILTERKEY: + if (field == AUDIT_FILTERKEY && !(_audit_syscalladded || _audit_permadded)) + return -19; + vlen = strlen(v); + if (field == AUDIT_FILTERKEY && + vlen > AUDIT_MAX_KEY_LEN) + return -11; + else if (vlen > PATH_MAX) + return -11; + rule->values[rule->field_count] = vlen; + offset = rule->buflen; + rule->buflen += vlen; + *rulep = realloc(rule, sizeof(*rule) + rule->buflen); + if (*rulep == NULL) { + free(rule); + audit_msg(LOG_ERR, "Cannot realloc memory!\n"); + return -3; + } else { + rule = *rulep; + } + strncpy(&rule->buf[offset], v, vlen); + + break; + case AUDIT_ARCH: + if (_audit_syscalladded) + return -3; + if (!(op == AUDIT_NOT_EQUAL || op == AUDIT_EQUAL)) + return -13; + if (isdigit((char)*(v))) { + int machine; + + errno = 0; + _audit_elf = strtoul(v, NULL, 0); + if (errno) + return -5; + + // Make sure we have a valid mapping + machine = audit_elf_to_machine(_audit_elf); + if (machine < 0) + return -5; + } + else { + const char *arch=v; + unsigned int machine, elf; + machine = audit_determine_machine(arch); + /* OK, we have the machine type, now convert + to elf. */ + elf = audit_machine_to_elf(machine); + if (elf == 0) + return -5; + + _audit_elf = elf; + } + rule->values[rule->field_count] = _audit_elf; + _audit_archadded = 1; + break; + case AUDIT_PERM: + if (flags != AUDIT_FILTER_EXIT) + return -7; + else if (op != AUDIT_EQUAL) + return -13; + else { + unsigned int i, len, val = 0; + + len = strlen(v); + if (len > 4) + return -11; + + for (i = 0; i < len; i++) { + switch (tolower(v[i])) { + case 'r': + val |= AUDIT_PERM_READ; + break; + case 'w': + val |= AUDIT_PERM_WRITE; + break; + case 'x': + val |= AUDIT_PERM_EXEC; + break; + case 'a': + val |= AUDIT_PERM_ATTR; + break; + default: + return -14; + } + } + rule->values[rule->field_count] = val; + } + break; + case AUDIT_FILETYPE: + if (!(flags == AUDIT_FILTER_EXIT || flags == AUDIT_FILTER_ENTRY)) + return -17; + rule->values[rule->field_count] = + audit_name_to_ftype(v); + if ((int)rule->values[rule->field_count] < 0) { + return -16; + } + break; + case AUDIT_ARG0...AUDIT_ARG3: + vlen = strlen(v); + if (isdigit((char)*(v))) + rule->values[rule->field_count] = + strtoul(v, NULL, 0); + else if (vlen >= 2 && *(v)=='-' && + (isdigit((char)*(v+1)))) + rule->values[rule->field_count] = + strtol(v, NULL, 0); + else + return -21; + break; + case AUDIT_DEVMAJOR...AUDIT_INODE: + case AUDIT_SUCCESS: + if (flags != AUDIT_FILTER_EXIT) + return -7; + /* fallthrough */ + default: + if (field == AUDIT_INODE) { + if (!(op == AUDIT_NOT_EQUAL || + op == AUDIT_EQUAL)) + return -13; + } + + if (field == AUDIT_PPID && !(flags == AUDIT_FILTER_EXIT + || flags == AUDIT_FILTER_ENTRY)) + return -17; + + if (!isdigit((char)*(v))) + return -21; + + if (field == AUDIT_INODE) + rule->values[rule->field_count] = + strtoul(v, NULL, 0); + else + rule->values[rule->field_count] = + strtol(v, NULL, 0); + break; + } + rule->field_count++; + return 0; +} + +void audit_rule_free_data(struct audit_rule_data *rule) +{ + free(rule); +} + +static int audit_name_to_uid(const char *name, uid_t *uid) +{ + struct passwd *pw; + + pw = getpwnam(name); + if (pw == NULL) + return 1; + + memset(pw->pw_passwd, ' ', strlen(pw->pw_passwd)); + *uid = pw->pw_uid; + return 0; +} + +static int audit_name_to_gid(const char *name, gid_t *gid) +{ + struct group *gr; + + gr = getgrnam(name); + if (gr == NULL) + return 1; + + *gid = gr->gr_gid; + return 0; +} + +int audit_detect_machine(void) +{ + struct utsname uts; + if (uname(&uts) == 0) +// strcpy(uts.machine, "x86_64"); + return audit_name_to_machine(uts.machine); + return -1; +} +hidden_def(audit_detect_machine) + +#ifndef NO_TABLES +void audit_number_to_errmsg(int errnumber, const char *opt) +{ + unsigned int i; + + for (i = 0; i < sizeof(err_msgtab)/sizeof(struct msg_tab); i++) { + if (err_msgtab[i].key == errnumber) { + switch (err_msgtab[i].position) + { + case 0: + fprintf(stderr, "%s\n", + err_msgtab[i].cvalue); + break; + case 1: + fprintf(stderr, "%s %s\n", opt, + err_msgtab[i].cvalue); + break; + case 2: + fprintf(stderr, "%s %s\n", + err_msgtab[i].cvalue, opt); + break; + default: + break; + } + return; + } + } +} +#endif diff --git a/framework/src/audit/lib/libaudit.h b/framework/src/audit/lib/libaudit.h new file mode 100644 index 00000000..8f96c5db --- /dev/null +++ b/framework/src/audit/lib/libaudit.h @@ -0,0 +1,584 @@ +/* libaudit.h -- + * Copyright 2004-2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Rickard E. (Rik) Faith + */ +#ifndef _LIBAUDIT_H_ +#define _LIBAUDIT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include +#include +#include +#include +#include + + +/* Audit message types as of 2.6.29 kernel: + * 1000 - 1099 are for commanding the audit system + * 1100 - 1199 user space trusted application messages + * 1200 - 1299 messages internal to the audit daemon + * 1300 - 1399 audit event messages + * 1400 - 1499 kernel SE Linux use + * 1500 - 1599 AppArmor events + * 1600 - 1699 kernel crypto events + * 1700 - 1799 kernel anomaly records + * 1800 - 1899 kernel integrity labels and related events + * 1800 - 1999 future kernel use + * 2001 - 2099 unused (kernel) + * 2100 - 2199 user space anomaly records + * 2200 - 2299 user space actions taken in response to anomalies + * 2300 - 2399 user space generated LSPP events + * 2400 - 2499 user space crypto events + * 2500 - 2599 user space virtualization management events + * 2600 - 2999 future user space (maybe integrity labels and related events) + */ + +#define AUDIT_FIRST_USER_MSG 1100 /* First user space message */ +#define AUDIT_LAST_USER_MSG 1199 /* Last user space message */ +#define AUDIT_USER_AUTH 1100 /* User system access authentication */ +#define AUDIT_USER_ACCT 1101 /* User system access authorization */ +#define AUDIT_USER_MGMT 1102 /* User acct attribute change */ +#define AUDIT_CRED_ACQ 1103 /* User credential acquired */ +#define AUDIT_CRED_DISP 1104 /* User credential disposed */ +#define AUDIT_USER_START 1105 /* User session start */ +#define AUDIT_USER_END 1106 /* User session end */ +#define AUDIT_USER_AVC 1107 /* User space avc message */ +#define AUDIT_USER_CHAUTHTOK 1108 /* User acct password or pin changed */ +#define AUDIT_USER_ERR 1109 /* User acct state error */ +#define AUDIT_CRED_REFR 1110 /* User credential refreshed */ +#define AUDIT_USYS_CONFIG 1111 /* User space system config change */ +#define AUDIT_USER_LOGIN 1112 /* User has logged in */ +#define AUDIT_USER_LOGOUT 1113 /* User has logged out */ +#define AUDIT_ADD_USER 1114 /* User account added */ +#define AUDIT_DEL_USER 1115 /* User account deleted */ +#define AUDIT_ADD_GROUP 1116 /* Group account added */ +#define AUDIT_DEL_GROUP 1117 /* Group account deleted */ +#define AUDIT_DAC_CHECK 1118 /* User space DAC check results */ +#define AUDIT_CHGRP_ID 1119 /* User space group ID changed */ +#define AUDIT_TEST 1120 /* Used for test success messages */ +#define AUDIT_TRUSTED_APP 1121 /* Trusted app msg - freestyle text */ +#define AUDIT_USER_SELINUX_ERR 1122 /* SE Linux user space error */ +#define AUDIT_USER_CMD 1123 /* User shell command and args */ +#define AUDIT_USER_TTY 1124 /* Non-ICANON TTY input meaning */ +#define AUDIT_CHUSER_ID 1125 /* Changed user ID supplemental data */ +#define AUDIT_GRP_AUTH 1126 /* Authentication for group password */ +#define AUDIT_SYSTEM_BOOT 1127 /* System boot */ +#define AUDIT_SYSTEM_SHUTDOWN 1128 /* System shutdown */ +#define AUDIT_SYSTEM_RUNLEVEL 1129 /* System runlevel change */ +#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ +#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ +#define AUDIT_GRP_MGMT 1132 /* Group account attr was modified */ +#define AUDIT_GRP_CHAUTHTOK 1133 /* Group acct password or pin changed */ +#define AUDIT_MAC_CHECK 1134 /* User space MAC decision results */ + +#define AUDIT_FIRST_DAEMON 1200 +#define AUDIT_LAST_DAEMON 1299 +#define AUDIT_DAEMON_RECONFIG 1204 /* Auditd should reconfigure */ +#define AUDIT_DAEMON_ROTATE 1205 /* Auditd should rotate logs */ +#define AUDIT_DAEMON_RESUME 1206 /* Auditd should resume logging */ +#define AUDIT_DAEMON_ACCEPT 1207 /* Auditd accepted remote connection */ +#define AUDIT_DAEMON_CLOSE 1208 /* Auditd closed remote connection */ + +#define AUDIT_FIRST_EVENT 1300 +#define AUDIT_LAST_EVENT 1399 + +#define AUDIT_FIRST_SELINUX 1400 +#define AUDIT_LAST_SELINUX 1499 + +#define AUDIT_FIRST_APPARMOR 1500 +#define AUDIT_LAST_APPARMOR 1599 +#ifndef AUDIT_AA +#define AUDIT_AA 1500 /* Not upstream yet */ +#define AUDIT_APPARMOR_AUDIT 1501 +#define AUDIT_APPARMOR_ALLOWED 1502 +#define AUDIT_APPARMOR_DENIED 1503 +#define AUDIT_APPARMOR_HINT 1504 +#define AUDIT_APPARMOR_STATUS 1505 +#define AUDIT_APPARMOR_ERROR 1506 +#endif + +#define AUDIT_FIRST_KERN_CRYPTO_MSG 1600 +#define AUDIT_LAST_KERN_CRYPTO_MSG 1699 + +#define AUDIT_FIRST_KERN_ANOM_MSG 1700 +#define AUDIT_LAST_KERN_ANOM_MSG 1799 + +#define AUDIT_INTEGRITY_FIRST_MSG 1800 +#define AUDIT_INTEGRITY_LAST_MSG 1899 +#ifndef AUDIT_INTEGRITY_DATA +#define AUDIT_INTEGRITY_DATA 1800 /* Data integrity verification */ +#define AUDIT_INTEGRITY_METADATA 1801 // Metadata integrity verification +#define AUDIT_INTEGRITY_STATUS 1802 /* Integrity enable status */ +#define AUDIT_INTEGRITY_HASH 1803 /* Integrity HASH type */ +#define AUDIT_INTEGRITY_PCR 1804 /* PCR invalidation msgs */ +#define AUDIT_INTEGRITY_RULE 1805 /* Policy rule */ +#endif + +#define AUDIT_FIRST_ANOM_MSG 2100 +#define AUDIT_LAST_ANOM_MSG 2199 +#define AUDIT_ANOM_LOGIN_FAILURES 2100 // Failed login limit reached +#define AUDIT_ANOM_LOGIN_TIME 2101 // Login attempted at bad time +#define AUDIT_ANOM_LOGIN_SESSIONS 2102 // Max concurrent sessions reached +#define AUDIT_ANOM_LOGIN_ACCT 2103 // Login attempted to watched acct +#define AUDIT_ANOM_LOGIN_LOCATION 2104 // Login from forbidden location +#define AUDIT_ANOM_MAX_DAC 2105 // Max DAC failures reached +#define AUDIT_ANOM_MAX_MAC 2106 // Max MAC failures reached +#define AUDIT_ANOM_AMTU_FAIL 2107 // AMTU failure +#define AUDIT_ANOM_RBAC_FAIL 2108 // RBAC self test failure +#define AUDIT_ANOM_RBAC_INTEGRITY_FAIL 2109 // RBAC file integrity failure +#define AUDIT_ANOM_CRYPTO_FAIL 2110 // Crypto system test failure +#define AUDIT_ANOM_ACCESS_FS 2111 // Access of file or dir +#define AUDIT_ANOM_EXEC 2112 // Execution of file +#define AUDIT_ANOM_MK_EXEC 2113 // Make an executable +#define AUDIT_ANOM_ADD_ACCT 2114 // Adding an acct +#define AUDIT_ANOM_DEL_ACCT 2115 // Deleting an acct +#define AUDIT_ANOM_MOD_ACCT 2116 // Changing an acct +#define AUDIT_ANOM_ROOT_TRANS 2117 // User became root + +#define AUDIT_FIRST_ANOM_RESP 2200 +#define AUDIT_LAST_ANOM_RESP 2299 +#define AUDIT_RESP_ANOMALY 2200 /* Anomaly not reacted to */ +#define AUDIT_RESP_ALERT 2201 /* Alert email was sent */ +#define AUDIT_RESP_KILL_PROC 2202 /* Kill program */ +#define AUDIT_RESP_TERM_ACCESS 2203 /* Terminate session */ +#define AUDIT_RESP_ACCT_REMOTE 2204 /* Acct locked from remote access*/ +#define AUDIT_RESP_ACCT_LOCK_TIMED 2205 /* User acct locked for time */ +#define AUDIT_RESP_ACCT_UNLOCK_TIMED 2206 /* User acct unlocked from time */ +#define AUDIT_RESP_ACCT_LOCK 2207 /* User acct was locked */ +#define AUDIT_RESP_TERM_LOCK 2208 /* Terminal was locked */ +#define AUDIT_RESP_SEBOOL 2209 /* Set an SE Linux boolean */ +#define AUDIT_RESP_EXEC 2210 /* Execute a script */ +#define AUDIT_RESP_SINGLE 2211 /* Go to single user mode */ +#define AUDIT_RESP_HALT 2212 /* take the system down */ + +#define AUDIT_FIRST_USER_LSPP_MSG 2300 +#define AUDIT_LAST_USER_LSPP_MSG 2399 +#define AUDIT_USER_ROLE_CHANGE 2300 /* User changed to a new role */ +#define AUDIT_ROLE_ASSIGN 2301 /* Admin assigned user to role */ +#define AUDIT_ROLE_REMOVE 2302 /* Admin removed user from role */ +#define AUDIT_LABEL_OVERRIDE 2303 /* Admin is overriding a label */ +#define AUDIT_LABEL_LEVEL_CHANGE 2304 /* Object's level was changed */ +#define AUDIT_USER_LABELED_EXPORT 2305 /* Object exported with label */ +#define AUDIT_USER_UNLABELED_EXPORT 2306 /* Object exported without label */ +#define AUDIT_DEV_ALLOC 2307 /* Device was allocated */ +#define AUDIT_DEV_DEALLOC 2308 /* Device was deallocated */ +#define AUDIT_FS_RELABEL 2309 /* Filesystem relabeled */ +#define AUDIT_USER_MAC_POLICY_LOAD 2310 /* Userspc daemon loaded policy */ +#define AUDIT_ROLE_MODIFY 2311 /* Admin modified a role */ +#define AUDIT_USER_MAC_CONFIG_CHANGE 2312 /* Change made to MAC policy */ + +#define AUDIT_FIRST_CRYPTO_MSG 2400 +#define AUDIT_CRYPTO_TEST_USER 2400 /* Crypto test results */ +#define AUDIT_CRYPTO_PARAM_CHANGE_USER 2401 /* Crypto attribute change */ +#define AUDIT_CRYPTO_LOGIN 2402 /* Logged in as crypto officer */ +#define AUDIT_CRYPTO_LOGOUT 2403 /* Logged out from crypto */ +#define AUDIT_CRYPTO_KEY_USER 2404 /* Create,delete,negotiate */ +#define AUDIT_CRYPTO_FAILURE_USER 2405 /* Fail decrypt,encrypt,randomiz */ +#define AUDIT_CRYPTO_REPLAY_USER 2406 /* Crypto replay detected */ +#define AUDIT_CRYPTO_SESSION 2407 /* Record parameters set during + TLS session establishment */ +#define AUDIT_CRYPTO_IKE_SA 2408 /* Record parameters related to + IKE SA */ +#define AUDIT_CRYPTO_IPSEC_SA 2409 /* Record parameters related to + IPSEC SA */ + +#define AUDIT_LAST_CRYPTO_MSG 2499 + +#define AUDIT_FIRST_VIRT_MSG 2500 +#define AUDIT_VIRT_CONTROL 2500 /* Start, Pause, Stop VM */ +#define AUDIT_VIRT_RESOURCE 2501 /* Resource assignment */ +#define AUDIT_VIRT_MACHINE_ID 2502 /* Binding of label to VM */ + +#define AUDIT_LAST_VIRT_MSG 2599 + +#ifndef AUDIT_FIRST_USER_MSG2 +#define AUDIT_FIRST_USER_MSG2 2100 /* More userspace messages */ +#define AUDIT_LAST_USER_MSG2 2999 +#endif + +/* New kernel event definitions since 2.6.30 */ +#ifndef AUDIT_SET_FEATURE +#define AUDIT_SET_FEATURE 1018 /* Turn an audit feature on or off */ +#endif + +#ifndef AUDIT_GET_FEATURE +#define AUDIT_GET_FEATURE 1019 /* Get which features are enabled */ +#endif + +#ifndef AUDIT_MMAP +#define AUDIT_MMAP 1323 /* Descriptor and flags in mmap */ +#endif + +#ifndef AUDIT_NETFILTER_PKT +#define AUDIT_NETFILTER_PKT 1324 /* Packets traversing netfilter chains */ +#endif +#ifndef AUDIT_NETFILTER_CFG +#define AUDIT_NETFILTER_CFG 1325 /* Netfilter chain modifications */ +#endif + +#ifndef AUDIT_SECCOMP +#define AUDIT_SECCOMP 1326 /* Secure Computing event */ +#endif + +#ifndef AUDIT_PROCTITLE +#define AUDIT_PROCTITLE 1327 /* Process Title info */ +#endif + +#undef AUDIT_FEATURE_CHANGE +#ifndef AUDIT_FEATURE_CHANGE +#define AUDIT_FEATURE_CHANGE 1328 /* Audit feature changed value */ +#endif + +#ifndef AUDIT_ANOM_LINK +#define AUDIT_ANOM_LINK 1702 /* Suspicious use of file links */ +#endif + +/* This is related to the filterkey patch */ +#define AUDIT_KEY_SEPARATOR 0x01 + +/* These are used in filter control */ +#define AUDIT_FILTER_EXCLUDE AUDIT_FILTER_TYPE +#define AUDIT_FILTER_MASK 0x07 /* Mask to get actual filter */ +#define AUDIT_FILTER_UNSET 0x80 /* This value means filter is unset */ + +/* Defines for interfield comparison update */ +#ifndef AUDIT_OBJ_UID +#define AUDIT_OBJ_UID 109 +#endif +#ifndef AUDIT_OBJ_GID +#define AUDIT_OBJ_GID 110 +#endif +#ifndef AUDIT_FIELD_COMPARE +#define AUDIT_FIELD_COMPARE 111 +#endif + +#ifndef AUDIT_COMPARE_UID_TO_OBJ_UID +#define AUDIT_COMPARE_UID_TO_OBJ_UID 1 +#endif +#ifndef AUDIT_COMPARE_GID_TO_OBJ_GID +#define AUDIT_COMPARE_GID_TO_OBJ_GID 2 +#endif +#ifndef AUDIT_COMPARE_EUID_TO_OBJ_UID +#define AUDIT_COMPARE_EUID_TO_OBJ_UID 3 +#endif +#ifndef AUDIT_COMPARE_EGID_TO_OBJ_GID +#define AUDIT_COMPARE_EGID_TO_OBJ_GID 4 +#endif +#ifndef AUDIT_COMPARE_AUID_TO_OBJ_UID +#define AUDIT_COMPARE_AUID_TO_OBJ_UID 5 +#endif +#ifndef AUDIT_COMPARE_SUID_TO_OBJ_UID +#define AUDIT_COMPARE_SUID_TO_OBJ_UID 6 +#endif +#ifndef AUDIT_COMPARE_SGID_TO_OBJ_GID +#define AUDIT_COMPARE_SGID_TO_OBJ_GID 7 +#endif +#ifndef AUDIT_COMPARE_FSUID_TO_OBJ_UID +#define AUDIT_COMPARE_FSUID_TO_OBJ_UID 8 +#endif +#ifndef AUDIT_COMPARE_FSGID_TO_OBJ_GID +#define AUDIT_COMPARE_FSGID_TO_OBJ_GID 9 +#endif +#ifndef AUDIT_COMPARE_UID_TO_AUID +#define AUDIT_COMPARE_UID_TO_AUID 10 +#endif +#ifndef AUDIT_COMPARE_UID_TO_EUID +#define AUDIT_COMPARE_UID_TO_EUID 11 +#endif +#ifndef AUDIT_COMPARE_UID_TO_FSUID +#define AUDIT_COMPARE_UID_TO_FSUID 12 +#endif +#ifndef AUDIT_COMPARE_UID_TO_SUID +#define AUDIT_COMPARE_UID_TO_SUID 13 +#endif +#ifndef AUDIT_COMPARE_AUID_TO_FSUID +#define AUDIT_COMPARE_AUID_TO_FSUID 14 +#endif +#ifndef AUDIT_COMPARE_AUID_TO_SUID +#define AUDIT_COMPARE_AUID_TO_SUID 15 +#endif +#ifndef AUDIT_COMPARE_AUID_TO_EUID +#define AUDIT_COMPARE_AUID_TO_EUID 16 +#endif +#ifndef AUDIT_COMPARE_EUID_TO_SUID +#define AUDIT_COMPARE_EUID_TO_SUID 17 +#endif +#ifndef AUDIT_COMPARE_EUID_TO_FSUID +#define AUDIT_COMPARE_EUID_TO_FSUID 18 +#endif +#ifndef AUDIT_COMPARE_SUID_TO_FSUID +#define AUDIT_COMPARE_SUID_TO_FSUID 19 +#endif +#ifndef AUDIT_COMPARE_GID_TO_EGID +#define AUDIT_COMPARE_GID_TO_EGID 20 +#endif +#ifndef AUDIT_COMPARE_GID_TO_FSGID +#define AUDIT_COMPARE_GID_TO_FSGID 21 +#endif +#ifndef AUDIT_COMPARE_GID_TO_SGID +#define AUDIT_COMPARE_GID_TO_SGID 22 +#endif +#ifndef AUDIT_COMPARE_EGID_TO_FSGID +#define AUDIT_COMPARE_EGID_TO_FSGID 23 +#endif +#ifndef AUDIT_COMPARE_EGID_TO_SGID +#define AUDIT_COMPARE_EGID_TO_SGID 24 +#endif +#ifndef AUDIT_COMPARE_SGID_TO_FSGID +#define AUDIT_COMPARE_SGID_TO_FSGID 25 +#endif + +#ifndef EM_ARM +#define EM_ARM 40 +#endif +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#endif + +#ifndef AUDIT_ARCH_AARCH64 +#define AUDIT_ARCH_AARCH64 (EM_AARCH64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) +#endif + +#ifndef AUDIT_ARCH_PPC64LE +#define AUDIT_ARCH_PPC64LE (EM_PPC64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE) +#endif + +////////////////////////////////////////////////////// +// This is an external ABI. Any changes in here will +// likely affect pam_loginuid. There might be other +// apps that use this low level interface, but I don't +// know of any. +// +/* data structure for who signaled the audit daemon */ +struct audit_sig_info { + uid_t uid; + pid_t pid; + char ctx[0]; +}; + +/* defines for audit subsystem */ +#define MAX_AUDIT_MESSAGE_LENGTH 8970 // PATH_MAX*2+CONTEXT_SIZE*2+11+256+1 +struct audit_message { + struct nlmsghdr nlh; + char data[MAX_AUDIT_MESSAGE_LENGTH]; +}; + +// internal - forward declaration +struct daemon_conf; + +struct audit_reply { + int type; + int len; + struct nlmsghdr *nlh; + struct audit_message msg; + + /* Using a union to compress this structure since only one of + * the following should be valid for any packet. */ + union { + struct audit_status *status; + struct audit_rule_data *ruledata; + struct audit_login *login; + char *message; + struct nlmsgerr *error; + struct audit_sig_info *signal_info; + struct daemon_conf *conf; +#if HAVE_DECL_AUDIT_FEATURE_VERSION + struct audit_features *features; +#endif + }; +}; + +// +// End of ABI control +////////////////////////////////////////////////////// + +////////////////////////////////////////////////////// +// audit dispatcher interface +// +/* audit_dispatcher_header: This header is versioned. If anything gets + * added to it, it must go at the end and the version number bumped. + * This MUST BE fixed size for compatibility. If you are going to add + * new member then add them into _structure_ part. + */ +struct audit_dispatcher_header { + uint32_t ver; /* The version of this protocol */ + uint32_t hlen; /* Header length */ + uint32_t type; /* Message type */ + uint32_t size; /* Size of data following the header */ +}; + +#define AUDISP_PROTOCOL_VER 0 + +/////////////////////////////////////////////////// +// Libaudit API +// + +/* This is the machine type list */ +typedef enum { + MACH_X86=0, + MACH_86_64, + MACH_IA64, + MACH_PPC64, + MACH_PPC, + MACH_S390X, + MACH_S390, + MACH_ALPHA, + MACH_ARM, + MACH_AARCH64, + MACH_PPC64LE +} machine_t; + +/* These are the valid audit failure tunable enum values */ +typedef enum { + FAIL_IGNORE=0, + FAIL_LOG, + FAIL_TERMINATE +} auditfail_t; + +/* Messages */ +typedef enum { MSG_STDERR, MSG_SYSLOG, MSG_QUIET } message_t; +typedef enum { DBG_NO, DBG_YES } debug_message_t; +void set_aumessage_mode(message_t mode, debug_message_t debug); + +/* General */ +typedef enum { GET_REPLY_BLOCKING=0, GET_REPLY_NONBLOCKING } reply_t; +extern int audit_open(void); +extern void audit_close(int fd); +extern int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, + int peek); +extern uid_t audit_getloginuid(void); +extern int audit_setloginuid(uid_t uid); +extern int audit_detect_machine(void); +extern int audit_determine_machine(const char *arch); + +/* Translation functions */ +extern int audit_name_to_field(const char *field); +extern const char *audit_field_to_name(int field); +extern int audit_name_to_syscall(const char *sc, int machine); +extern const char *audit_syscall_to_name(int sc, int machine); +extern int audit_name_to_flag(const char *flag); +extern const char *audit_flag_to_name(int flag); +extern int audit_name_to_action(const char *action); +extern const char *audit_action_to_name(int action); +extern int audit_name_to_msg_type(const char *msg_type); +extern const char *audit_msg_type_to_name(int msg_type); +extern int audit_name_to_machine(const char *machine); +extern const char *audit_machine_to_name(int machine); +extern unsigned int audit_machine_to_elf(int machine); +extern int audit_elf_to_machine(unsigned int elf); +extern const char *audit_operator_to_symbol(int op); +extern int audit_name_to_errno(const char *error); +extern const char *audit_errno_to_name(int error); +extern int audit_name_to_ftype(const char *name); +extern const char *audit_ftype_to_name(int ftype); +extern void audit_number_to_errmsg(int errnumber, const char *opt); + +/* AUDIT_GET */ +extern int audit_request_status(int fd); +extern int audit_is_enabled(int fd); +extern int get_auditfail_action(auditfail_t *failmode); +extern int audit_request_features(int fd); + +/* AUDIT_SET */ +typedef enum { WAIT_NO, WAIT_YES } rep_wait_t; +extern int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode); +extern int audit_set_enabled(int fd, uint32_t enabled); +extern int audit_set_failure(int fd, uint32_t failure); +extern int audit_set_rate_limit(int fd, uint32_t limit); +extern int audit_set_backlog_limit(int fd, uint32_t limit); +int audit_set_backlog_wait_time(int fd, uint32_t bwt); +extern int audit_set_feature(int fd, unsigned feature, unsigned value, unsigned lock); +extern int audit_set_loginuid_immutable(int fd); + +/* AUDIT_LIST_RULES */ +extern int audit_request_rules_list_data(int fd); + +/* SIGNAL_INFO */ +extern int audit_request_signal_info(int fd); + +/* AUDIT_WATCH */ +extern int audit_update_watch_perms(struct audit_rule_data *rule, int perms); +extern int audit_add_watch(struct audit_rule_data **rulep, const char *path); +extern int audit_add_dir(struct audit_rule_data **rulep, const char *path); +extern int audit_add_watch_dir(int type, struct audit_rule_data **rulep, + const char *path); +extern int audit_trim_subtrees(int fd); +extern int audit_make_equivalent(int fd, const char *mount_point, + const char *subtree); + +/* AUDIT_ADD_RULE */ +extern int audit_add_rule_data(int fd, struct audit_rule_data *rule, + int flags, int action); + +/* AUDIT_DEL_RULE */ +extern int audit_delete_rule_data(int fd, struct audit_rule_data *rule, + int flags, int action); + +/* The following are for standard formatting of messages */ +extern int audit_value_needs_encoding(const char *str, unsigned int len); +extern char *audit_encode_value(char *final,const char *buf,unsigned int size); +extern char *audit_encode_nv_string(const char *name, const char *value, + unsigned int vlen); +extern int audit_log_user_message(int audit_fd, int type, const char *message, + const char *hostname, const char *addr, const char *tty, int result); +extern int audit_log_user_comm_message(int audit_fd, int type, + const char *message, const char *comm, const char *hostname, + const char *addr, const char *tty, int result); +extern int audit_log_acct_message(int audit_fd, int type, const char *pgname, + const char *op, const char *name, unsigned int id, + const char *host, const char *addr, const char *tty, int result); +extern int audit_log_user_avc_message(int audit_fd, int type, + const char *message, const char *hostname, const char *addr, + const char *tty, uid_t uid); +extern int audit_log_semanage_message(int audit_fd, int type, + const char *pgname, const char *op, const char *name, unsigned int id, + const char *new_seuser, const char *new_role, const char *new_range, + const char *old_seuser, const char *old_role, const char *old_range, + const char *host, const char *addr, + const char *tty, int result); +extern int audit_log_user_command(int audit_fd, int type, const char *command, + const char *tty, int result); + +/* Rule-building helper functions */ +extern int audit_rule_syscall_data(struct audit_rule_data *rule, int scall); +extern int audit_rule_syscallbyname_data(struct audit_rule_data *rule, + const char *scall); +/* Note that the following function takes a **, where audit_rule_fieldpair() + * takes just a *. That structure may need to be reallocated as a result of + * adding new fields */ +extern int audit_rule_fieldpair_data(struct audit_rule_data **rulep, + const char *pair, int flags); +extern int audit_rule_interfield_comp_data(struct audit_rule_data **rulep, + const char *pair, int flags); +extern void audit_rule_free_data(struct audit_rule_data *rule); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/lib/lookup_table.c b/framework/src/audit/lib/lookup_table.c new file mode 100644 index 00000000..6dc63d4b --- /dev/null +++ b/framework/src/audit/lib/lookup_table.c @@ -0,0 +1,359 @@ +/* lookup_table.c -- + * Copyright 2004-2008,2012-13 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Rickard E. (Rik) Faith + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "libaudit.h" +#include "gen_tables.h" +#include "private.h" + +#ifndef NO_TABLES +#ifdef WITH_ALPHA +#include "alpha_tables.h" +#endif +#ifdef WITH_ARM +#include "arm_tables.h" +#endif +#ifdef WITH_AARCH64 +#include "aarch64_tables.h" +#endif +#include "i386_tables.h" +#include "ia64_tables.h" +#include "ppc_tables.h" +#include "s390_tables.h" +#include "s390x_tables.h" +#include "x86_64_tables.h" +#include "errtabs.h" +#include "ftypetabs.h" +#include "fieldtabs.h" +#endif +#include "msg_typetabs.h" +#include "actiontabs.h" +#include "flagtabs.h" +#include "machinetabs.h" +#include "optabs.h" + +struct int_transtab { + int key; + unsigned int lvalue; +}; + +static const struct int_transtab elftab[] = { + { MACH_X86, AUDIT_ARCH_I386 }, + { MACH_86_64, AUDIT_ARCH_X86_64 }, + { MACH_IA64, AUDIT_ARCH_IA64 }, + { MACH_PPC64, AUDIT_ARCH_PPC64 }, + { MACH_PPC64LE, AUDIT_ARCH_PPC64LE}, + { MACH_PPC, AUDIT_ARCH_PPC }, + { MACH_S390X, AUDIT_ARCH_S390X }, + { MACH_S390, AUDIT_ARCH_S390 }, +#ifdef WITH_ALPHA + { MACH_ALPHA, AUDIT_ARCH_ALPHA }, +#endif +#ifdef WITH_ARM + { MACH_ARM, AUDIT_ARCH_ARM }, +#endif +#ifdef WITH_AARCH64 + { MACH_AARCH64, AUDIT_ARCH_AARCH64}, +#endif +}; +#define AUDIT_ELF_NAMES (sizeof(elftab)/sizeof(elftab[0])) + +int audit_name_to_field(const char *field) +{ +#ifndef NO_TABLES + int res; + + if (field_s2i(field, &res) != 0) + return res; +#endif + return -1; +} +hidden_def(audit_name_to_field) + +const char *audit_field_to_name(int field) +{ +#ifndef NO_TABLES + return field_i2s(field); +#else + return NULL; +#endif +} + +int audit_name_to_syscall(const char *sc, int machine) +{ + int res, found = 0; + + switch (machine) + { +#ifndef NO_TABLES + case MACH_X86: + found = i386_syscall_s2i(sc, &res); + break; + case MACH_86_64: + found = x86_64_syscall_s2i(sc, &res); + break; + case MACH_IA64: + found = ia64_syscall_s2i(sc, &res); + break; + case MACH_PPC64: + case MACH_PPC64LE: + case MACH_PPC: + found = ppc_syscall_s2i(sc, &res); + break; + case MACH_S390X: + found = s390x_syscall_s2i(sc, &res); + break; + case MACH_S390: + found = s390_syscall_s2i(sc, &res); + break; +#ifdef WITH_ALPHA + case MACH_ALPHA: + found = alpha_syscall_s2i(sc, &res); + break; +#endif +#ifdef WITH_ARM + case MACH_ARM: + found = arm_syscall_s2i(sc, &res); + break; +#endif +#ifdef WITH_AARCH64 + case MACH_AARCH64: + found = aarch64_syscall_s2i(sc, &res); + break; +#endif +#endif + default: + return -1; + } + if (found) + return res; + return -1; +} +hidden_def(audit_name_to_syscall) + +const char *audit_syscall_to_name(int sc, int machine) +{ +#ifndef NO_TABLES + switch (machine) + { + case MACH_X86: + return i386_syscall_i2s(sc); + case MACH_86_64: + return x86_64_syscall_i2s(sc); + case MACH_IA64: + return ia64_syscall_i2s(sc); + case MACH_PPC64: + case MACH_PPC64LE: + case MACH_PPC: + return ppc_syscall_i2s(sc); + case MACH_S390X: + return s390x_syscall_i2s(sc); + case MACH_S390: + return s390_syscall_i2s(sc); +#ifdef WITH_ALPHA + case MACH_ALPHA: + return alpha_syscall_i2s(sc); +#endif +#ifdef WITH_ARM + case MACH_ARM: + return arm_syscall_i2s(sc); +#endif +#ifdef WITH_AARCH64 + case MACH_AARCH64: + return aarch64_syscall_i2s(sc); +#endif + } +#endif + return NULL; +} + +int audit_name_to_flag(const char *flag) +{ + int res; + + if (flag_s2i(flag, &res) != 0) + return res; + return -1; +} + +const char *audit_flag_to_name(int flag) +{ + return flag_i2s(flag); +} + +int audit_name_to_action(const char *action) +{ + int res; + + if (action_s2i(action, &res) != 0) + return res; + return -1; +} + +const char *audit_action_to_name(int action) +{ + return action_i2s(action); +} + +// On the critical path for ausearch parser +int audit_name_to_msg_type(const char *msg_type) +{ + int rc; + + if (msg_type_s2i(msg_type, &rc) != 0) + return rc; + + /* Take a stab at converting */ + if (strncmp(msg_type, "UNKNOWN[", 8) == 0) { + int len; + char buf[8]; + const char *end = strchr(msg_type + 8, ']'); + if (end == NULL) + return -1; + + len = end - (msg_type + 8); + if (len > 7) + len = 7; + memset(buf, 0, sizeof(buf)); + strncpy(buf, msg_type + 8, len); + errno = 0; + return strtol(buf, NULL, 10); + } else if (isdigit(*msg_type)) { + errno = 0; + return strtol(msg_type, NULL, 10); + } + + return -1; +} +hidden_def(audit_name_to_msg_type) + +const char *audit_msg_type_to_name(int msg_type) +{ + return msg_type_i2s(msg_type); +} +hidden_def(audit_msg_type_to_name) + +int audit_name_to_machine(const char *machine) +{ + int res; + + if (machine_s2i(machine, &res) != 0) + return res; + return -1; +} +hidden_def(audit_name_to_machine) + +const char *audit_machine_to_name(int machine) +{ + return machine_i2s(machine); +} + +unsigned int audit_machine_to_elf(int machine) +{ + unsigned int i; + + for (i = 0; i < AUDIT_ELF_NAMES; i++) + if (elftab[i].key == machine) + return elftab[i].lvalue; + return 0; +} +hidden_def(audit_machine_to_elf) + +int audit_elf_to_machine(unsigned int elf) +{ + unsigned int i; + + for (i = 0; i < AUDIT_ELF_NAMES; i++) + if (elftab[i].lvalue == elf) return elftab[i].key; + return -1; +} +hidden_def(audit_elf_to_machine) + +const char *audit_operator_to_symbol(int op) +{ + return op_i2s(op); +} +hidden_def(audit_operator_to_symbol) + +/* This function returns 0 on error, otherwise the converted value */ +int audit_name_to_errno(const char *error) +{ +#ifndef NO_TABLES + int rc, minus = 1; + + if (*error == '-') { + minus = -1; + error++; + } + if (err_s2i(error, &rc) == 0) + rc = 0; + + return rc*minus; +#else + return 0; +#endif +} +hidden_def(audit_name_to_errno) + +/* This function does not handle negative numbers yet */ +const char *audit_errno_to_name(int error) +{ +#ifndef NO_TABLES + if (error < 0) + return NULL; + + return err_i2s(error); +#else + return NULL; +#endif +} + +int audit_name_to_ftype(const char *name) +{ + int res; + +#ifndef NO_TABLES + if (ftype_s2i(name, &res) != 0) + return res; +#endif + return -1; +} +hidden_def(audit_name_to_ftype) + +const char *audit_ftype_to_name(int ftype) +{ +#ifndef NO_TABLES + return ftype_i2s(ftype); +#else + return NULL; +#endif +} + diff --git a/framework/src/audit/lib/machinetab.h b/framework/src/audit/lib/machinetab.h new file mode 100644 index 00000000..27c69420 --- /dev/null +++ b/framework/src/audit/lib/machinetab.h @@ -0,0 +1,47 @@ +/* machine.h -- + * Copyright 2005,2006,2009,2012,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(MACH_X86, "i386" ) +_S(MACH_X86, "i486" ) +_S(MACH_X86, "i586" ) +_S(MACH_X86, "i686" ) +_S(MACH_86_64, "x86_64" ) +_S(MACH_IA64, "ia64" ) +_S(MACH_PPC64, "ppc64" ) +_S(MACH_PPC64LE, "ppc64le") +_S(MACH_PPC, "ppc" ) +_S(MACH_S390X, "s390x" ) +_S(MACH_S390, "s390" ) +#ifdef WITH_ALPHA +_S(MACH_ALPHA, "alpha" ) +#endif +#ifdef WITH_ARM +_S(MACH_ARM, "armeb" ) +_S(MACH_ARM, "arm" ) +_S(MACH_ARM, "armv5tejl") +_S(MACH_ARM, "armv5tel") +_S(MACH_ARM, "armv6l") +_S(MACH_ARM, "armv7l") +#endif +#ifdef WITH_AARCH64 +_S(MACH_AARCH64, "aarch64" ) +#endif diff --git a/framework/src/audit/lib/message.c b/framework/src/audit/lib/message.c new file mode 100644 index 00000000..85e9e64b --- /dev/null +++ b/framework/src/audit/lib/message.c @@ -0,0 +1,59 @@ +/* message.c -- + * Copyright 2004, 2005 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include "libaudit.h" +#include "private.h" + +/* The message mode refers to where informational messages go + 0 - stderr, 1 - syslog, 2 - quiet. The default is quiet. */ +static message_t message_mode = MSG_QUIET; +static debug_message_t debug_message = DBG_NO; + +void set_aumessage_mode(message_t mode, debug_message_t debug) +{ + message_mode = mode; + debug_message = debug; +} + +void audit_msg(int priority, const char *fmt, ...) +{ + va_list ap; + + if (message_mode == MSG_QUIET) + return; + + if (priority == LOG_DEBUG && debug_message == DBG_NO) + return; + + va_start(ap, fmt); + if (message_mode == MSG_SYSLOG) + vsyslog(priority, fmt, ap); + else { + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + } + va_end( ap ); +} +hidden_def(audit_msg) diff --git a/framework/src/audit/lib/msg_typetab.h b/framework/src/audit/lib/msg_typetab.h new file mode 100644 index 00000000..c8e47564 --- /dev/null +++ b/framework/src/audit/lib/msg_typetab.h @@ -0,0 +1,214 @@ +/* msg_typetab.h -- + * Copyright 2005-07,2009-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +/* + * This table is arranged from lowest number to highest number. The + * items that are commented out are for completeness. The audit + * daemon filters these and they never show up in the logs, therefore + * they are not needed for reporting. Or they have been deprecated for + * a long time. + */ +//_S(AUDIT_GET, "GET" ) +//_S(AUDIT_SET, "SET" ) +//_S(AUDIT_LIST, "LIST" ) +//_S(AUDIT_ADD, "ADD" ) +//_S(AUDIT_DEL, "DEL" ) +_S(AUDIT_USER, "USER" ) +_S(AUDIT_LOGIN, "LOGIN" ) +//_S(AUDIT_SIGNAL_INFO, "SIGNAL_INFO" ) +//_S(AUDIT_ADD_RULE, "ADD_RULE" ) +//_S(AUDIT_DEL_RULE, "DEL_RULE" ) +//_S(AUDIT_LIST_RULES, "LIST_RULES" ) +//_S(AUDIT_TRIM, "TRIM" ) +//_S(AUDIT_MAKE_EQUIV, "MAKE_EQUIV" ) +//_S(AUDIT_TTY_GET, "TTY_GET" ) +//_S(AUDIT_TTY_SET, "TTY_SET" ) +//_S(AUDIT_SET_FEATURE, "SET_FEATURE" ) +//_S(AUDIT_GET_FEATURE, "GET_FEATURE" ) +_S(AUDIT_USER_AUTH, "USER_AUTH" ) +_S(AUDIT_USER_ACCT, "USER_ACCT" ) +_S(AUDIT_USER_MGMT, "USER_MGMT" ) +_S(AUDIT_CRED_ACQ, "CRED_ACQ" ) +_S(AUDIT_CRED_DISP, "CRED_DISP" ) +_S(AUDIT_USER_START, "USER_START" ) +_S(AUDIT_USER_END, "USER_END" ) +_S(AUDIT_USER_AVC, "USER_AVC" ) +_S(AUDIT_USER_CHAUTHTOK, "USER_CHAUTHTOK" ) +_S(AUDIT_USER_ERR, "USER_ERR" ) +_S(AUDIT_CRED_REFR, "CRED_REFR" ) +_S(AUDIT_USYS_CONFIG, "USYS_CONFIG" ) +_S(AUDIT_USER_LOGIN, "USER_LOGIN" ) +_S(AUDIT_USER_LOGOUT, "USER_LOGOUT" ) +_S(AUDIT_ADD_USER, "ADD_USER" ) +_S(AUDIT_DEL_USER, "DEL_USER" ) +_S(AUDIT_ADD_GROUP, "ADD_GROUP" ) +_S(AUDIT_DEL_GROUP, "DEL_GROUP" ) +_S(AUDIT_DAC_CHECK, "DAC_CHECK" ) +_S(AUDIT_CHGRP_ID, "CHGRP_ID" ) +_S(AUDIT_TEST, "TEST" ) +_S(AUDIT_TRUSTED_APP, "TRUSTED_APP" ) +_S(AUDIT_USER_SELINUX_ERR, "USER_SELINUX_ERR" ) +_S(AUDIT_USER_CMD, "USER_CMD" ) +_S(AUDIT_USER_TTY, "USER_TTY" ) +_S(AUDIT_CHUSER_ID, "CHUSER_ID" ) +_S(AUDIT_GRP_AUTH, "GRP_AUTH" ) +_S(AUDIT_MAC_CHECK, "MAC_CHECK" ) +_S(AUDIT_SYSTEM_BOOT, "SYSTEM_BOOT" ) +_S(AUDIT_SYSTEM_SHUTDOWN, "SYSTEM_SHUTDOWN" ) +_S(AUDIT_SYSTEM_RUNLEVEL, "SYSTEM_RUNLEVEL" ) +_S(AUDIT_SERVICE_START, "SERVICE_START" ) +_S(AUDIT_SERVICE_STOP, "SERVICE_STOP" ) +_S(AUDIT_GRP_MGMT, "GRP_MGMT" ) +_S(AUDIT_GRP_CHAUTHTOK, "GRP_CHAUTHTOK" ) +_S(AUDIT_DAEMON_START, "DAEMON_START" ) +_S(AUDIT_DAEMON_END, "DAEMON_END" ) +_S(AUDIT_DAEMON_ABORT, "DAEMON_ABORT" ) +_S(AUDIT_DAEMON_CONFIG, "DAEMON_CONFIG" ) +//_S(AUDIT_DAEMON_RECONFIG, "DAEMON_RECONFIG" ) +_S(AUDIT_DAEMON_ROTATE, "DAEMON_ROTATE" ) +_S(AUDIT_DAEMON_RESUME, "DAEMON_RESUME" ) +_S(AUDIT_DAEMON_ACCEPT, "DAEMON_ACCEPT" ) +_S(AUDIT_DAEMON_CLOSE, "DAEMON_CLOSE" ) +_S(AUDIT_SYSCALL, "SYSCALL" ) +//_S(AUDIT_FS_WATCH, "FS_WATCH" ) +_S(AUDIT_PATH, "PATH" ) +_S(AUDIT_IPC, "IPC" ) +_S(AUDIT_SOCKETCALL, "SOCKETCALL" ) +_S(AUDIT_CONFIG_CHANGE, "CONFIG_CHANGE" ) +_S(AUDIT_SOCKADDR, "SOCKADDR" ) +_S(AUDIT_CWD, "CWD" ) +//_S(AUDIT_FS_INODE, "FS_INODE" ) +_S(AUDIT_EXECVE, "EXECVE" ) +_S(AUDIT_IPC_SET_PERM, "IPC_SET_PERM" ) +_S(AUDIT_MQ_OPEN, "MQ_OPEN" ) +_S(AUDIT_MQ_SENDRECV, "MQ_SENDRECV" ) +_S(AUDIT_MQ_NOTIFY, "MQ_NOTIFY" ) +_S(AUDIT_MQ_GETSETATTR, "MQ_GETSETATTR" ) +_S(AUDIT_KERNEL_OTHER, "KERNEL_OTHER" ) +_S(AUDIT_FD_PAIR, "FD_PAIR" ) +_S(AUDIT_OBJ_PID, "OBJ_PID" ) +_S(AUDIT_TTY, "TTY" ) +_S(AUDIT_EOE, "EOE" ) +_S(AUDIT_BPRM_FCAPS, "BPRM_FCAPS" ) +_S(AUDIT_CAPSET, "CAPSET" ) +_S(AUDIT_MMAP, "MMAP" ) +_S(AUDIT_NETFILTER_PKT, "NETFILTER_PKT" ) +_S(AUDIT_NETFILTER_CFG, "NETFILTER_CFG" ) +_S(AUDIT_SECCOMP, "SECCOMP" ) +_S(AUDIT_PROCTITLE, "PROCTITLE" ) +_S(AUDIT_FEATURE_CHANGE, "FEATURE_CHANGE" ) +_S(AUDIT_AVC, "AVC" ) +_S(AUDIT_SELINUX_ERR, "SELINUX_ERR" ) +_S(AUDIT_AVC_PATH, "AVC_PATH" ) +_S(AUDIT_MAC_POLICY_LOAD, "MAC_POLICY_LOAD" ) +_S(AUDIT_MAC_STATUS, "MAC_STATUS" ) +_S(AUDIT_MAC_CONFIG_CHANGE, "MAC_CONFIG_CHANGE" ) +_S(AUDIT_MAC_UNLBL_ALLOW, "MAC_UNLBL_ALLOW" ) +_S(AUDIT_MAC_CIPSOV4_ADD, "MAC_CIPSOV4_ADD" ) +_S(AUDIT_MAC_CIPSOV4_DEL, "MAC_CIPSOV4_DEL" ) +_S(AUDIT_MAC_MAP_ADD, "MAC_MAP_ADD" ) +_S(AUDIT_MAC_MAP_DEL, "MAC_MAP_DEL" ) +_S(AUDIT_MAC_IPSEC_ADDSA, "MAC_IPSEC_ADDSA" ) +_S(AUDIT_MAC_IPSEC_DELSA, "MAC_IPSEC_DELSA" ) +_S(AUDIT_MAC_IPSEC_ADDSPD, "MAC_IPSEC_ADDSPD" ) +_S(AUDIT_MAC_IPSEC_DELSPD, "MAC_IPSEC_DELSPD" ) +_S(AUDIT_MAC_IPSEC_EVENT, "MAC_IPSEC_EVENT" ) +_S(AUDIT_MAC_UNLBL_STCADD, "MAC_UNLBL_STCADD" ) +_S(AUDIT_MAC_UNLBL_STCDEL, "MAC_UNLBL_STCDEL" ) +_S(AUDIT_ANOM_PROMISCUOUS, "ANOM_PROMISCUOUS" ) +_S(AUDIT_ANOM_ABEND, "ANOM_ABEND" ) +_S(AUDIT_ANOM_LINK, "ANOM_LINK" ) +_S(AUDIT_INTEGRITY_DATA, "INTEGRITY_DATA" ) +_S(AUDIT_INTEGRITY_METADATA, "INTEGRITY_METADATA" ) +_S(AUDIT_INTEGRITY_STATUS, "INTEGRITY_STATUS" ) +_S(AUDIT_INTEGRITY_HASH, "INTEGRITY_HASH" ) +_S(AUDIT_INTEGRITY_PCR, "INTEGRITY_PCR" ) +_S(AUDIT_INTEGRITY_RULE, "INTEGRITY_RULE" ) + +#ifdef WITH_APPARMOR +_S(AUDIT_AA, "APPARMOR" ) +_S(AUDIT_APPARMOR_AUDIT, "APPARMOR_AUDIT" ) +_S(AUDIT_APPARMOR_ALLOWED, "APPARMOR_ALLOWED" ) +_S(AUDIT_APPARMOR_DENIED, "APPARMOR_DENIED" ) +_S(AUDIT_APPARMOR_HINT, "APPARMOR_HINT" ) +_S(AUDIT_APPARMOR_STATUS, "APPARMOR_STATUS" ) +_S(AUDIT_APPARMOR_ERROR, "APPARMOR_ERROR" ) +#endif +_S(AUDIT_KERNEL, "KERNEL" ) +_S(AUDIT_ANOM_LOGIN_FAILURES, "ANOM_LOGIN_FAILURES" ) +_S(AUDIT_ANOM_LOGIN_TIME, "ANOM_LOGIN_TIME" ) +_S(AUDIT_ANOM_LOGIN_SESSIONS, "ANOM_LOGIN_SESSIONS" ) +_S(AUDIT_ANOM_LOGIN_ACCT, "ANOM_LOGIN_ACCT" ) +_S(AUDIT_ANOM_LOGIN_LOCATION, "ANOM_LOGIN_LOCATION" ) +_S(AUDIT_ANOM_MAX_DAC, "ANOM_MAX_DAC" ) +_S(AUDIT_ANOM_MAX_MAC, "ANOM_MAX_MAC" ) +_S(AUDIT_ANOM_AMTU_FAIL, "ANOM_AMTU_FAIL" ) +_S(AUDIT_ANOM_RBAC_FAIL, "ANOM_RBAC_FAIL" ) +_S(AUDIT_ANOM_RBAC_INTEGRITY_FAIL, "ANOM_RBAC_INTEGRITY_FAIL" ) +_S(AUDIT_ANOM_CRYPTO_FAIL, "ANOM_CRYPTO_FAIL" ) +_S(AUDIT_ANOM_ACCESS_FS, "ANOM_ACCESS_FS" ) +_S(AUDIT_ANOM_EXEC, "ANOM_EXEC" ) +_S(AUDIT_ANOM_MK_EXEC, "ANOM_MK_EXEC" ) +_S(AUDIT_ANOM_ADD_ACCT, "ANOM_ADD_ACCT" ) +_S(AUDIT_ANOM_DEL_ACCT, "ANOM_DEL_ACCT" ) +_S(AUDIT_ANOM_MOD_ACCT, "ANOM_MOD_ACCT" ) +_S(AUDIT_ANOM_ROOT_TRANS, "ANOM_ROOT_TRANS" ) +_S(AUDIT_RESP_ANOMALY, "RESP_ANOMALY" ) +_S(AUDIT_RESP_ALERT, "RESP_ALERT" ) +_S(AUDIT_RESP_KILL_PROC, "RESP_KILL_PROC" ) +_S(AUDIT_RESP_TERM_ACCESS, "RESP_TERM_ACCESS" ) +_S(AUDIT_RESP_ACCT_REMOTE, "RESP_ACCT_REMOTE" ) +_S(AUDIT_RESP_ACCT_LOCK_TIMED, "RESP_ACCT_LOCK_TIMED" ) +_S(AUDIT_RESP_ACCT_UNLOCK_TIMED, "RESP_ACCT_UNLOCK_TIMED" ) +_S(AUDIT_RESP_ACCT_LOCK, "RESP_ACCT_LOCK" ) +_S(AUDIT_RESP_TERM_LOCK, "RESP_TERM_LOCK" ) +_S(AUDIT_RESP_SEBOOL, "RESP_SEBOOL" ) +_S(AUDIT_RESP_EXEC, "RESP_EXEC" ) +_S(AUDIT_RESP_SINGLE, "RESP_SINGLE" ) +_S(AUDIT_RESP_HALT, "RESP_HALT" ) +_S(AUDIT_USER_ROLE_CHANGE, "USER_ROLE_CHANGE" ) +_S(AUDIT_ROLE_ASSIGN, "ROLE_ASSIGN" ) +_S(AUDIT_ROLE_REMOVE, "ROLE_REMOVE" ) +_S(AUDIT_LABEL_OVERRIDE, "LABEL_OVERRIDE" ) +_S(AUDIT_LABEL_LEVEL_CHANGE, "LABEL_LEVEL_CHANGE" ) +_S(AUDIT_USER_LABELED_EXPORT, "USER_LABELED_EXPORT" ) +_S(AUDIT_USER_UNLABELED_EXPORT, "USER_UNLABELED_EXPORT" ) +_S(AUDIT_DEV_ALLOC, "DEV_ALLOC" ) +_S(AUDIT_DEV_DEALLOC, "DEV_DEALLOC" ) +_S(AUDIT_FS_RELABEL, "FS_RELABEL" ) +_S(AUDIT_USER_MAC_POLICY_LOAD, "USER_MAC_POLICY_LOAD" ) +_S(AUDIT_ROLE_MODIFY, "ROLE_MODIFY" ) +_S(AUDIT_USER_MAC_CONFIG_CHANGE, "USER_MAC_CONFIG_CHANGE" ) +_S(AUDIT_CRYPTO_TEST_USER, "CRYPTO_TEST_USER" ) +_S(AUDIT_CRYPTO_PARAM_CHANGE_USER, "CRYPTO_PARAM_CHANGE_USER" ) +_S(AUDIT_CRYPTO_LOGIN, "CRYPTO_LOGIN" ) +_S(AUDIT_CRYPTO_LOGOUT, "CRYPTO_LOGOUT" ) +_S(AUDIT_CRYPTO_KEY_USER, "CRYPTO_KEY_USER" ) +_S(AUDIT_CRYPTO_FAILURE_USER, "CRYPTO_FAILURE_USER" ) +_S(AUDIT_CRYPTO_REPLAY_USER, "CRYPTO_REPLAY_USER" ) +_S(AUDIT_CRYPTO_SESSION, "CRYPTO_SESSION" ) +_S(AUDIT_CRYPTO_IKE_SA, "CRYPTO_IKE_SA" ) +_S(AUDIT_CRYPTO_IPSEC_SA, "CRYPTO_IPSEC_SA" ) +_S(AUDIT_VIRT_CONTROL, "VIRT_CONTROL" ) +_S(AUDIT_VIRT_RESOURCE, "VIRT_RESOURCE" ) +_S(AUDIT_VIRT_MACHINE_ID, "VIRT_MACHINE_ID" ) + diff --git a/framework/src/audit/lib/netlink.c b/framework/src/audit/lib/netlink.c new file mode 100644 index 00000000..6c80c30c --- /dev/null +++ b/framework/src/audit/lib/netlink.c @@ -0,0 +1,299 @@ +/* netlink.c -- + * Copyright 2004, 2005, 2009, 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Rickard E. (Rik) Faith + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "private.h" + +#ifndef NETLINK_AUDIT +#define NETLINK_AUDIT 9 +#endif + +static int adjust_reply(struct audit_reply *rep, int len); +static int check_ack(int fd, int seq); + +/* + * This function opens a connection to the kernel's audit + * subsystem. You must be root for the call to succeed. On error, + * a negative value is returned. On success, the file descriptor is + * returned - which can be 0 or higher. + */ +int audit_open(void) +{ + int saved_errno; + int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); + + if (fd < 0) { + saved_errno = errno; + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) + audit_msg(LOG_ERR, + "Error - audit support not in kernel"); + else + audit_msg(LOG_ERR, + "Error opening audit netlink socket (%s)", + strerror(errno)); + errno = saved_errno; + return fd; + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + saved_errno = errno; + close(fd); + audit_msg(LOG_ERR, + "Error setting audit netlink socket CLOEXEC flag (%s)", + strerror(errno)); + errno = saved_errno; + return -1; + } + return fd; +} + + +void audit_close(int fd) +{ + if (fd >= 0) + close(fd); +} + + +/* + * This function returns -1 on error, 0 if error response received, + * and > 0 if packet OK. + */ +int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek) +{ + int len; + struct sockaddr_nl nladdr; + socklen_t nladdrlen = sizeof(nladdr); + + if (fd < 0) + return -EBADF; + + if (block == GET_REPLY_NONBLOCKING) + block = MSG_DONTWAIT; + +retry: + len = recvfrom(fd, &rep->msg, sizeof(rep->msg), block|peek, + (struct sockaddr*)&nladdr, &nladdrlen); + + if (len < 0) { + if (errno == EINTR) + goto retry; + if (errno != EAGAIN) { + int saved_errno = errno; + audit_msg(LOG_ERR, + "Error receiving audit netlink packet (%s)", + strerror(errno)); + errno = saved_errno; + } + return -errno; + } + if (nladdrlen != sizeof(nladdr)) { + audit_msg(LOG_ERR, + "Bad address size reading audit netlink socket"); + return -EPROTO; + } + if (nladdr.nl_pid) { + audit_msg(LOG_ERR, + "Spoofed packet received on audit netlink socket"); + return -EINVAL; + } + + len = adjust_reply(rep, len); + if (len == 0) + len = -errno; + return len; +} +hidden_def(audit_get_reply) + + +/* + * This function returns 0 on error and len on success. + */ +static int adjust_reply(struct audit_reply *rep, int len) +{ + rep->type = rep->msg.nlh.nlmsg_type; + rep->len = rep->msg.nlh.nlmsg_len; + rep->nlh = &rep->msg.nlh; + rep->status = NULL; + rep->ruledata = NULL; + rep->login = NULL; + rep->message = NULL; + rep->error = NULL; + rep->signal_info = NULL; + rep->conf = NULL; +#if HAVE_DECL_AUDIT_FEATURE_VERSION + rep->features = NULL; +#endif + if (!NLMSG_OK(rep->nlh, (unsigned int)len)) { + if (len == sizeof(rep->msg)) { + audit_msg(LOG_ERR, + "Netlink event from kernel is too big"); + errno = EFBIG; + } else { + audit_msg(LOG_ERR, + "Netlink message from kernel was not OK"); + errno = EBADE; + } + return 0; + } + + /* Next we'll set the data structure to point to msg.data. This is + * to avoid having to use casts later. */ + switch (rep->type) { + case NLMSG_ERROR: + rep->error = NLMSG_DATA(rep->nlh); + break; + case AUDIT_GET: + rep->status = NLMSG_DATA(rep->nlh); + break; +#if HAVE_DECL_AUDIT_FEATURE_VERSION + case AUDIT_GET_FEATURE: + rep->features = NLMSG_DATA(rep->nlh); + break; +#endif + case AUDIT_LIST_RULES: + rep->ruledata = NLMSG_DATA(rep->nlh); + break; + case AUDIT_USER: + case AUDIT_LOGIN: + case AUDIT_KERNEL: + case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: + case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: + case AUDIT_FIRST_EVENT...AUDIT_INTEGRITY_LAST_MSG: + rep->message = NLMSG_DATA(rep->nlh); + break; + case AUDIT_SIGNAL_INFO: + rep->signal_info = NLMSG_DATA(rep->nlh); + break; + } + return len; +} + + +/* + * Return values: success: positive non-zero sequence number + * error: -errno + * short: 0 + */ +int audit_send(int fd, int type, const void *data, unsigned int size) +{ + static int sequence = 0; + struct audit_message req; + int retval; + struct sockaddr_nl addr; + + /* Due to user space library callbacks, there's a chance that + a -1 for the fd could be passed. Just check for and handle it. */ + if (fd < 0) { + errno = EBADF; + return -errno; + } + + if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) { + errno = EINVAL; + return -errno; + } + + if (++sequence < 0) + sequence = 1; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_SPACE(size); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; + req.nlh.nlmsg_seq = sequence; + if (size && data) + memcpy(NLMSG_DATA(&req.nlh), data, size); + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + + do { + retval = sendto(fd, &req, req.nlh.nlmsg_len, 0, + (struct sockaddr*)&addr, sizeof(addr)); + } while (retval < 0 && errno == EINTR); + if (retval == (int)req.nlh.nlmsg_len) { + if ((retval = check_ack(fd, sequence)) == 0) + return sequence; + else + return retval; + } + if (retval < 0) + return -errno; + + return 0; +} +hidden_def(audit_send) + +/* + * This function will take a peek into the next packet and see if there's + * an error. If so, the error is returned and its non-zero. Otherwise a + * zero is returned indicating that we don't know of any problems. + */ +static int check_ack(int fd, int seq) +{ + int rc, retries = 80; + struct audit_reply rep; + struct pollfd pfd[1]; + +retry: + pfd[0].fd = fd; + pfd[0].events = POLLIN; + do { + rc = poll(pfd, 1, 500); /* .5 second */ + } while (rc < 0 && errno == EINTR); + + /* We don't look at rc from above as it doesn't matter. We are + * going to try to read nonblocking just in case packet shows up. */ + + /* NOTE: whatever is returned is treated as the errno */ + rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, MSG_PEEK); + if (rc == -EAGAIN && retries) { + retries--; + goto retry; + } else if (rc < 0) + return rc; + else if (rc == 0) + return -EINVAL; /* This can't happen anymore */ + else if (rc > 0 && rep.type == NLMSG_ERROR) { + int error = rep.error->error; + /* Eat the message */ + (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + + /* NLMSG_ERROR can indicate success, only report nonzero */ + if (error) { + errno = -error; + return error; + } + } + return 0; +} + diff --git a/framework/src/audit/lib/optab.h b/framework/src/audit/lib/optab.h new file mode 100644 index 00000000..bddac253 --- /dev/null +++ b/framework/src/audit/lib/optab.h @@ -0,0 +1,31 @@ +/* optab.h -- + * Copyright 2005-07 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(AUDIT_EQUAL, "=" ) +_S(AUDIT_NOT_EQUAL, "!=" ) +_S(AUDIT_GREATER_THAN, ">" ) +_S(AUDIT_GREATER_THAN_OR_EQUAL, ">=" ) +_S(AUDIT_LESS_THAN, "<" ) +_S(AUDIT_LESS_THAN_OR_EQUAL, "<=" ) +_S(AUDIT_BIT_MASK, "&" ) +_S(AUDIT_BIT_TEST, "&=" ) + diff --git a/framework/src/audit/lib/ppc_table.h b/framework/src/audit/lib/ppc_table.h new file mode 100644 index 00000000..5a22f8bc --- /dev/null +++ b/framework/src/audit/lib/ppc_table.h @@ -0,0 +1,379 @@ +/* ppc_table.h -- + * Copyright 2005-09,2011-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Note from include/powerpc/unistd.h + */ + +_S(1, "exit") +_S(2, "fork") +_S(3, "read") +_S(4, "write") +_S(5, "open") +_S(6, "close") +_S(7, "waitpid") +_S(8, "creat") +_S(9, "link") +_S(10, "unlink") +_S(11, "execve") +_S(12, "chdir") +_S(13, "time") +_S(14, "mknod") +_S(15, "chmod") +_S(16, "lchown") +_S(17, "break") +_S(18, "oldstat") +_S(19, "lseek") +_S(20, "getpid") +_S(21, "mount") +_S(22, "umount") +_S(23, "setuid") +_S(24, "getuid") +_S(25, "stime") +_S(26, "ptrace") +_S(27, "alarm") +_S(28, "oldfstat") +_S(29, "pause") +_S(30, "utime") +_S(31, "stty") +_S(32, "gtty") +_S(33, "access") +_S(34, "nice") +_S(35, "ftime") +_S(36, "sync") +_S(37, "kill") +_S(38, "rename") +_S(39, "mkdir") +_S(40, "rmdir") +_S(41, "dup") +_S(42, "pipe") +_S(43, "times") +_S(44, "prof") +_S(45, "brk") +_S(46, "setgid") +_S(47, "getgid") +_S(48, "signal") +_S(49, "geteuid") +_S(50, "getegid") +_S(51, "acct") +_S(52, "umount2") +_S(53, "lock") +_S(54, "ioctl") +_S(55, "fcntl") +_S(56, "mpx") +_S(57, "setpgid") +_S(58, "ulimit") +_S(59, "oldolduname") +_S(60, "umask") +_S(61, "chroot") +_S(62, "ustat") +_S(63, "dup2") +_S(64, "getppid") +_S(65, "getpgrp") +_S(66, "setsid") +_S(67, "sigaction") +_S(68, "sgetmask") +_S(69, "ssetmask") +_S(70, "setreuid") +_S(71, "setregid") +_S(72, "sigsuspend") +_S(73, "sigpending") +_S(74, "sethostname") +_S(75, "setrlimit") +_S(76, "getrlimit") +_S(77, "getrusage") +_S(78, "gettimeofday") +_S(79, "settimeofday") +_S(80, "getgroups") +_S(81, "setgroups") +_S(82, "select") +_S(83, "symlink") +_S(84, "oldlstat") +_S(85, "readlink") +_S(86, "uselib") +_S(87, "swapon") +_S(88, "reboot") +_S(89, "readdir") +_S(90, "mmap") +_S(91, "munmap") +_S(92, "truncate") +_S(93, "ftruncate") +_S(94, "fchmod") +_S(95, "fchown") +_S(96, "getpriority") +_S(97, "setpriority") +_S(98, "profil") +_S(99, "statfs") +_S(100, "fstatfs") +_S(101, "ioperm") +_S(102, "socketcall") +_S(103, "syslog") +_S(104, "setitimer") +_S(105, "getitimer") +_S(106, "stat") +_S(107, "lstat") +_S(108, "fstat") +_S(109, "olduname") +_S(110, "iopl") +_S(111, "vhangup") +_S(112, "idle") +_S(113, "vm86") +_S(114, "wait4") +_S(115, "swapoff") +_S(116, "sysinfo") +_S(117, "ipc") +_S(118, "fsync") +_S(119, "sigreturn") +_S(120, "clone") +_S(121, "setdomainname") +_S(122, "uname") +_S(123, "modify_ldt") +_S(124, "adjtimex") +_S(125, "mprotect") +_S(126, "sigprocmask") +_S(127, "create_module") +_S(128, "init_module") +_S(129, "delete_module") +_S(130, "get_kernel_syms") +_S(131, "quotactl") +_S(132, "getpgid") +_S(133, "fchdir") +_S(134, "bdflush") +_S(135, "sysfs") +_S(136, "personality") +_S(137, "afs_syscall") +_S(138, "setfsuid") +_S(139, "setfsgid") +_S(140, "_llseek") +_S(141, "getdents") +_S(142, "_newselect") +_S(143, "flock") +_S(144, "msync") +_S(145, "readv") +_S(146, "writev") +_S(147, "getsid") +_S(148, "fdatasync") +_S(149, "_sysctl") +_S(150, "mlock") +_S(151, "munlock") +_S(152, "mlockall") +_S(153, "munlockall") +_S(154, "sched_setparam") +_S(155, "sched_getparam") +_S(156, "sched_setscheduler") +_S(157, "sched_getscheduler") +_S(158, "sched_yield") +_S(159, "sched_get_priority_max") +_S(160, "sched_get_priority_min") +_S(161, "sched_rr_get_interval") +_S(162, "nanosleep") +_S(163, "mremap") +_S(164, "setresuid") +_S(165, "getresuid") +_S(166, "query_module") +_S(167, "poll") +_S(168, "nfsservctl") +_S(169, "setresgid") +_S(170, "getresgid") +_S(171, "prctl") +_S(172, "rt_sigreturn") +_S(173, "rt_sigaction") +_S(174, "rt_sigprocmask") +_S(175, "rt_sigpending") +_S(176, "rt_sigtimedwait") +_S(177, "rt_sigqueueinfo") +_S(178, "rt_sigsuspend") +_S(179, "pread") +_S(180, "pwrite") +_S(181, "chown") +_S(182, "getcwd") +_S(183, "capget") +_S(184, "capset") +_S(185, "sigaltstack") +_S(186, "sendfile") +_S(187, "getpmsg") +_S(188, "putpmsg") +_S(189, "vfork") +_S(190, "ugetrlimit") +_S(191, "readahead") +_S(192, "mmap2") +_S(193, "truncate64") +_S(194, "ftruncate64") +_S(195, "stat64") +_S(196, "lstat64") +_S(197, "fstat64") +_S(198, "pciconfig_read") +_S(199, "pciconfig_write") +_S(200, "pciconfig_iobase") +_S(201, "multiplexer") +_S(202, "getdents64") +_S(203, "pivot_root") +_S(204, "fcntl64") +_S(205, "madvise") +_S(206, "mincore") +_S(207, "gettid") +_S(208, "tkill") +_S(209, "setxattr") +_S(210, "lsetxattr") +_S(211, "fsetxattr") +_S(212, "getxattr") +_S(213, "lgetxattr") +_S(214, "fgetxattr") +_S(215, "listxattr") +_S(216, "llistxattr") +_S(217, "flistxattr") +_S(218, "removexattr") +_S(219, "lremovexattr") +_S(220, "fremovexattr") +_S(221, "futex") +_S(222, "sched_setaffinity") +_S(223, "sched_getaffinity") +_S(225, "tuxcall") +_S(226, "sendfile64") +_S(227, "io_setup") +_S(228, "io_destroy") +_S(229, "io_getevents") +_S(230, "io_submit") +_S(231, "io_cancel") +_S(232, "set_tid_address") +_S(233, "fadvise64") +_S(234, "exit_group") +_S(235, "lookup_dcookie") +_S(236, "epoll_create") +_S(237, "epoll_ctl") +_S(238, "epoll_wait") +_S(239, "remap_file_pages") +_S(240, "timer_create") +_S(241, "timer_settime") +_S(242, "timer_gettime") +_S(243, "timer_getoverrun") +_S(244, "timer_delete") +_S(245, "clock_settime") +_S(246, "clock_gettime") +_S(247, "clock_getres") +_S(248, "clock_nanosleep") +_S(249, "swapcontext") +_S(250, "tgkill") +_S(251, "utimes") +_S(252, "statfs64") +_S(253, "fstatfs64") +_S(254, "fadvise64_64") +_S(255, "rtas") +_S(262, "mq_open") +_S(263, "mq_unlink") +_S(264, "mq_timedsend") +_S(265, "mq_timedreceive") +_S(266, "mq_notify") +_S(267, "mq_getsetattr") +_S(268, "kexec_load") +_S(269, "add_key") +_S(270, "request_key") +_S(271, "keyctl") +_S(272, "waitid") +_S(273, "ioprio_set") +_S(274, "ioprio_get") +_S(275, "inotify_init") +_S(276, "inotify_add_watch") +_S(277, "inotify_rm_watch") +_S(278, "spu_run") +_S(279, "spu_create") +_S(280, "pselect6") +_S(281, "ppoll") +_S(282, "unshare") +_S(283, "splice") +_S(284, "tee") +_S(285, "vmsplice") +_S(286, "openat") +_S(287, "mkdirat") +_S(288, "mknodat") +_S(289, "fchownat") +_S(290, "futimesat") +_S(291, "fstatat64") +_S(292, "unlinkat") +_S(293, "renameat") +_S(294, "linkat") +_S(295, "symlinkat") +_S(296, "readlinkat") +_S(297, "fchmodat") +_S(298, "faccessat") +_S(299, "get_robust_list") +_S(300, "set_robust_list") +_S(301, "move_pages") +_S(302, "getcpu") +_S(303, "epoll_pwait") +_S(304, "utimensat") +_S(305, "signalfd") +_S(306, "timerfd") +_S(307, "eventfd") +_S(308, "sync_file_range2") +_S(309, "fallocate") +_S(310, "subpage_prot") +_S(311, "timerfd_settime") +_S(312, "timerfd_gettime") +_S(313, "signalfd4") +_S(314, "eventfd2") +_S(315, "epoll_create1") +_S(316, "dup3") +_S(317, "pipe2") +_S(318, "inotify_init1") +_S(319, "perf_counter_open") +_S(320, "preadv") +_S(321, "pwritev") +_S(322, "rt_tgsigqueueinfo") +_S(323, "fanotify_init") +_S(324, "fanotify_mark") +_S(325, "prlimit64") +_S(326, "socket") +_S(327, "bind") +_S(328, "connect") +_S(329, "listen") +_S(330, "accept") +_S(331, "getsockname") +_S(332, "getpeername") +_S(333, "socketpair") +_S(334, "send") +_S(335, "sendto") +_S(336, "recv") +_S(337, "recvfrom") +_S(338, "shutdown") +_S(339, "setsockopt") +_S(340, "getsockopt") +_S(341, "sendmsg") +_S(342, "recvmsg") +_S(343, "recvmmsg") +_S(344, "accept4") +_S(345, "name_to_handle_at") +_S(346, "open_by_handle_at") +_S(347, "clock_adjtime") +_S(348, "syncfs") +_S(349, "sendmmsg") +_S(350, "setns") +_S(351, "process_vm_readv") +_S(352, "process_vm_writev") +_S(353, "finit_module") +_S(354, "kcmp") +_S(355, "sched_setattr") +_S(356, "sched_getattr") +_S(357, "renameat2") +_S(358, "seccomp") +_S(359, "getrandom") +_S(360, "memfd_create") +_S(361, "bpf") +_S(362, "execveat") +_S(363, "switch_endian") diff --git a/framework/src/audit/lib/private.h b/framework/src/audit/lib/private.h new file mode 100644 index 00000000..a0e3e35c --- /dev/null +++ b/framework/src/audit/lib/private.h @@ -0,0 +1,175 @@ +/* private.h -- + * Copyright 2005,2006,2009,2013-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +#ifndef _PRIVATE_H_ +#define _PRIVATE_H_ + +#include "dso.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { REAL_ERR, HIDE_IT } hide_t; + +/* Internal syslog messaging */ +void audit_msg(int priority, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 2, 3))); +#else + ; +#endif + +/* This structure is for protocol reference only. All fields are + packed and in network order (LSB first). */ +struct auditd_remote_message_wrapper { + /* The magic number shall never have LF (0x0a) as one of its bytes. */ + uint32_t magic; + /* Bumped when the layout of this structure changes. */ + uint8_t header_version; + /* The minimum support needed to understand this message type. + * Normally zero. */ + uint8_t message_version; + /* Upper 8 bits are generic type, see below. */ + uint32_t type; + /* Number of bytes that follow this header Must be 0..MAX_AUDIT_MESSAGE_LENGTH. */ + uint16_t length; + /* Copied from message to its reply. */ + uint32_t sequence_id; + /* The message follows for LENGTH bytes. */ +}; + +#define AUDIT_RMW_HEADER_SIZE 16 +/* The magic number shall never have LF (0x0a) as one of its bytes. */ +#define AUDIT_RMW_MAGIC 0xff0000feUL + +#define AUDIT_RMW_HEADER_VERSION 0 + +/* If set, this is a reply. */ +#define AUDIT_RMW_TYPE_REPLYMASK 0x40000000 +/* If set, this reply indicates a fatal error of some sort. */ +#define AUDIT_RMW_TYPE_FATALMASK 0x20000000 +/* If set, this reply indicates success but with some warnings. */ +#define AUDIT_RMW_TYPE_WARNMASK 0x10000000 +/* This part of the message type is the details for the above. */ +#define AUDIT_RMW_TYPE_DETAILMASK 0x0fffffff + +/* Version 0 messages. */ +#define AUDIT_RMW_TYPE_MESSAGE 0x00000000 +#define AUDIT_RMW_TYPE_HEARTBEAT 0x00000001 +#define AUDIT_RMW_TYPE_ACK 0x40000000 +#define AUDIT_RMW_TYPE_ENDING 0x40000001 +#define AUDIT_RMW_TYPE_DISKLOW 0x50000001 +#define AUDIT_RMW_TYPE_DISKFULL 0x60000001 +#define AUDIT_RMW_TYPE_DISKERROR 0x60000002 + +/* These next four should not be called directly. */ +#define _AUDIT_RMW_PUTN32(header,i,v) \ + header[i] = v & 0xff; \ + header[i+1] = (v>>8) & 0xff; \ + header[i+2] = (v>>16) & 0xff; \ + header[i+3] = (v>>24) & 0xff; +#define _AUDIT_RMW_PUTN16(header,i,v) \ + header[i] = v & 0xff; \ + header[i+1] = (v>>8) & 0xff; +#define _AUDIT_RMW_GETN32(header,i) \ + (((uint32_t)(header[i] & 0xFF)) | \ + (((uint32_t)(header[i+1] & 0xFF))<<8) | \ + (((uint32_t)(header[i+2] & 0xFF ))<<16) | \ + (((uint32_t)(header[i+3] & 0xFF))<<24)) +#define _AUDIT_RMW_GETN16(header,i) \ + ((uint32_t)(header[i] & 0xFF) | ((uint32_t)(header[i+1] & 0xFF)<<8)) + +/* For these, HEADER must by of type "unsigned char *" or "unsigned + char []" */ + +#define AUDIT_RMW_PACK_HEADER(header,mver,type,len,seq) \ + _AUDIT_RMW_PUTN32 (header,0, AUDIT_RMW_MAGIC); \ + header[4] = AUDIT_RMW_HEADER_VERSION; \ + header[5] = mver; \ + _AUDIT_RMW_PUTN32 (header,6, type); \ + _AUDIT_RMW_PUTN16 (header,10, len); \ + _AUDIT_RMW_PUTN32 (header,12, seq); + +#define AUDIT_RMW_IS_MAGIC(header,length) \ + (length >= 4 && _AUDIT_RMW_GETN32 (header,0) == AUDIT_RMW_MAGIC) + +#define AUDIT_RMW_UNPACK_HEADER(header,hver,mver,type,len,seq) \ + hver = header[4]; \ + mver = header[5]; \ + type = _AUDIT_RMW_GETN32 (header,6); \ + len = _AUDIT_RMW_GETN16 (header,10); \ + seq = _AUDIT_RMW_GETN32 (header,12); + +/* General */ +extern int audit_send(int fd, int type, const void *data, unsigned int size); + +// This is the main messaging function used internally +// Don't hide it, it used to be a part of the public API! +extern int audit_send_user_message(int fd, int type, hide_t hide_err, + const char *message); + +// libaudit.c +extern int _audit_permadded; +extern int _audit_archadded; +extern int _audit_syscalladded; +extern unsigned int _audit_elf; + +hidden_proto(audit_send_user_message); +hidden_proto(audit_add_watch_dir); +hidden_proto(audit_detect_machine); +hidden_proto(audit_request_status); +hidden_proto(audit_rule_syscall_data); +hidden_proto(audit_rule_syscallbyname_data); +hidden_proto(audit_set_feature); +hidden_proto(audit_request_features); + +// lookup_table.c +hidden_proto(audit_elf_to_machine); +hidden_proto(audit_machine_to_elf); +hidden_proto(audit_msg_type_to_name); +hidden_proto(audit_name_to_errno); +hidden_proto(audit_name_to_field); +hidden_proto(audit_name_to_machine); +hidden_proto(audit_name_to_msg_type); +hidden_proto(audit_name_to_syscall); +hidden_proto(audit_operator_to_symbol); +hidden_proto(audit_name_to_ftype); + +// netlink.c +hidden_proto(audit_get_reply); +hidden_proto(audit_send) + +// message.c +hidden_proto(audit_msg) + +// strsplit.c +char *audit_strsplit_r(char *s, char **savedpp); +char *audit_strsplit(char *s); +hidden_proto(audit_strsplit_r) +hidden_proto(audit_strsplit) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/lib/s390_table.h b/framework/src/audit/lib/s390_table.h new file mode 100644 index 00000000..d3cec4fb --- /dev/null +++ b/framework/src/audit/lib/s390_table.h @@ -0,0 +1,354 @@ +/* s390_table.h -- + * Copyright 2005-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(1, "exit") +_S(2, "fork") +_S(3, "read") +_S(4, "write") +_S(5, "open") +_S(6, "close") +_S(8, "creat") +_S(9, "link") +_S(10, "unlink") +_S(11, "execve") +_S(12, "chdir") +_S(13, "time") +_S(14, "mknod") +_S(15, "chmod") +_S(16, "lchown") +_S(19, "lseek") +_S(20, "getpid") +_S(21, "mount") +_S(22, "umount") +_S(23, "setuid") +_S(24, "getuid") +_S(25, "stime") +_S(26, "ptrace") +_S(27, "alarm") +_S(29, "pause") +_S(30, "utime") +_S(33, "access") +_S(34, "nice") +_S(36, "sync") +_S(37, "kill") +_S(38, "rename") +_S(39, "mkdir") +_S(40, "rmdir") +_S(41, "dup") +_S(42, "pipe") +_S(43, "times") +_S(45, "brk") +_S(46, "setgid") +_S(47, "getgid") +_S(48, "signal") +_S(49, "geteuid") +_S(50, "getegid") +_S(51, "acct") +_S(52, "umount2") +_S(54, "ioctl") +_S(55, "fcntl") +_S(57, "setpgid") +_S(60, "umask") +_S(61, "chroot") +_S(62, "ustat") +_S(63, "dup2") +_S(64, "getppid") +_S(65, "getpgrp") +_S(66, "setsid") +_S(67, "sigaction") +_S(70, "setreuid") +_S(71, "setregid") +_S(72, "sigsuspend") +_S(73, "sigpending") +_S(74, "sethostname") +_S(75, "setrlimit") +_S(76, "getrlimit") +_S(77, "getrusage") +_S(78, "gettimeofday") +_S(79, "settimeofday") +_S(80, "getgroups") +_S(81, "setgroups") +_S(83, "symlink") +_S(85, "readlink") +_S(86, "uselib") +_S(87, "swapon") +_S(88, "reboot") +_S(89, "readdir") +_S(90, "mmap") +_S(91, "munmap") +_S(92, "truncate") +_S(93, "ftruncate") +_S(94, "fchmod") +_S(95, "fchown") +_S(96, "getpriority") +_S(97, "setpriority") +_S(99, "statfs") +_S(100, "fstatfs") +_S(101, "ioperm") +_S(102, "socketcall") +_S(103, "syslog") +_S(104, "setitimer") +_S(105, "getitimer") +_S(106, "stat") +_S(107, "lstat") +_S(108, "fstat") +_S(111, "vhangup") +_S(112, "idle") +_S(114, "wait4") +_S(115, "swapoff") +_S(116, "sysinfo") +_S(117, "ipc") +_S(118, "fsync") +_S(119, "sigreturn") +_S(120, "clone") +_S(121, "setdomainname") +_S(122, "uname") +_S(124, "adjtimex") +_S(125, "mprotect") +_S(126, "sigprocmask") +_S(127, "create_module") +_S(128, "init_module") +_S(129, "delete_module") +_S(130, "get_kernel_syms") +_S(131, "quotactl") +_S(132, "getpgid") +_S(133, "fchdir") +_S(134, "bdflush") +_S(135, "sysfs") +_S(136, "personality") +_S(137, "afs_syscall") +_S(138, "setfsuid") +_S(139, "setfsgid") +_S(140, "_llseek") +_S(141, "getdents") +_S(142, "_newselect") +_S(143, "flock") +_S(144, "msync") +_S(145, "readv") +_S(146, "writev") +_S(147, "getsid") +_S(148, "fdatasync") +_S(149, "_sysctl") +_S(150, "mlock") +_S(151, "munlock") +_S(152, "mlockall") +_S(153, "munlockall") +_S(154, "sched_setparam") +_S(155, "sched_getparam") +_S(156, "sched_setscheduler") +_S(157, "sched_getscheduler") +_S(158, "sched_yield") +_S(159, "sched_get_priority_max") +_S(160, "sched_get_priority_min") +_S(161, "sched_rr_get_interval") +_S(162, "nanosleep") +_S(163, "mremap") +_S(164, "setresuid") +_S(165, "getresuid") +_S(167, "query_module") +_S(168, "poll") +_S(169, "nfsservctl") +_S(170, "setresgid") +_S(171, "getresgid") +_S(172, "prctl") +_S(173, "rt_sigreturn") +_S(174, "rt_sigaction") +_S(175, "rt_sigprocmask") +_S(176, "rt_sigpending") +_S(177, "rt_sigtimedwait") +_S(178, "rt_sigqueueinfo") +_S(179, "rt_sigsuspend") +_S(180, "pread") +_S(181, "pwrite") +_S(182, "chown") +_S(183, "getcwd") +_S(184, "capget") +_S(185, "capset") +_S(186, "sigaltstack") +_S(187, "sendfile") +_S(188, "getpmsg") +_S(189, "putpmsg") +_S(190, "vfork") +_S(191, "ugetrlimit") +_S(192, "mmap2") +_S(193, "truncate64") +_S(194, "ftruncate64") +_S(195, "stat64") +_S(196, "lstat64") +_S(197, "fstat64") +_S(198, "lchown32") +_S(199, "getuid32") +_S(200, "getgid32") +_S(201, "geteuid32") +_S(202, "getegid32") +_S(203, "setreuid32") +_S(204, "setregid32") +_S(205, "getgroups32") +_S(206, "setgroups32") +_S(207, "fchown32") +_S(208, "setresuid32") +_S(209, "getresuid32") +_S(210, "setresgid32") +_S(211, "getresgid32") +_S(212, "chown32") +_S(213, "setuid32") +_S(214, "setgid32") +_S(215, "setfsuid32") +_S(216, "setfsgid32") +_S(217, "pivot_root") +_S(218, "mincore") +_S(219, "madvise") +_S(220, "getdents64") +_S(221, "fcntl64") +_S(222, "readahead") +_S(223, "sendfile64") +_S(224, "setxattr") +_S(225, "lsetxattr") +_S(226, "fsetxattr") +_S(227, "getxattr") +_S(228, "lgetxattr") +_S(229, "fgetxattr") +_S(230, "listxattr") +_S(231, "llistxattr") +_S(232, "flistxattr") +_S(233, "removexattr") +_S(234, "lremovexattr") +_S(235, "fremovexattr") +_S(236, "gettid") +_S(237, "tkill") +_S(238, "futex") +_S(239, "sched_setaffinity") +_S(240, "sched_getaffinity") +_S(241, "tgkill") +//_S(242, "") +_S(243, "io_setup") +_S(244, "io_destroy") +_S(245, "io_getevents") +_S(246, "io_submit") +_S(247, "io_cancel") +_S(248, "exit_group") +_S(249, "epoll_create") +_S(250, "epoll_ctl") +_S(251, "epoll_wait") +_S(252, "set_tid_address") +_S(253, "fadvise64") +_S(254, "timer_create") +_S(255, "timer_settime") +_S(256, "timer_gettime") +_S(257, "timer_getoverrun") +_S(258, "timer_delete") +_S(259, "clock_settime") +_S(260, "clock_gettime") +_S(261, "clock_getres") +_S(262, "clock_nanosleep") +//_S(263, "") +_S(264, "fadvise64_64") +_S(265, "statfs64") +_S(266, "fstatfs64") +_S(267, "remap_file_pages") +//_S(268, "") +//_S(269, "") +//_S(270, "") +_S(271, "mq_open") +_S(272, "mq_unlink") +_S(273, "mq_timedsend") +_S(274, "mq_timedreceive") +_S(275, "mq_notify") +_S(276, "mq_getsetattr") +_S(277, "kexec_load") +_S(278, "add_key") +_S(279, "request_key") +_S(280, "keyctl") +_S(281, "waitid") +_S(282, "ioprio_set") +_S(283, "ioprio_get") +_S(284, "inotify_init") +_S(285, "inotify_add_watch") +_S(286, "inotify_rm_watch") +//_S(287, "") +_S(288, "openat") +_S(289, "mkdirat") +_S(290, "mknodat") +_S(291, "fchownat") +_S(292, "futimesat") +_S(293, "fstatat64") +_S(294, "unlinkat") +_S(295, "renameat") +_S(296, "linkat") +_S(297, "symlinkat") +_S(298, "readlinkat") +_S(299, "fchmodat") +_S(300, "faccessat") +_S(301, "pselect6") +_S(302, "ppoll") +_S(303, "unshare") +_S(304, "set_robust_list") +_S(305, "get_robust_list") +_S(306, "splice") +_S(307, "sync_file_range") +_S(308, "tee") +_S(309, "vmsplice") +//_S(310, "") +_S(311, "getcpu") +_S(312, "epoll_pwait") +_S(313, "utimes") +_S(314, "fallocate") +_S(315, "utimensat") +_S(316, "signalfd") +_S(317, "timerfd") +_S(318, "eventfd") +_S(319, "timerfd_create") +_S(320, "timerfd_settime") +_S(321, "timerfd_gettime") +_S(322, "signalfd4") +_S(323, "eventfd2") +_S(324, "inotify_init1") +_S(325, "pipe2") +_S(326, "dup3") +_S(327, "epoll_create1") +_S(328, "preadv") +_S(329, "pwritev") +_S(330, "rt_tgsigqueueinfo") +_S(331, "perf_event_open") +_S(332, "fanotify_init") +_S(333, "fanotify_mark") +_S(334, "prlimit64") +_S(335, "name_to_handle_at") +_S(336, "open_by_handle_at") +_S(337, "clock_adjtime") +_S(338, "syncfs") +_S(339, "setns") +_S(340, "process_vm_readv") +_S(341, "process_vm_writev") +_S(342, "s390_runtime_instr") +_S(343, "kcmp") +_S(344, "finit_module") +_S(345, "sched_setattr") +_S(346, "sched_getattr") +_S(347, "renameat2") +_S(348, "seccomp") +_S(349, "getrandom") +_S(350, "memfd_create") +_S(351, "bpf") +_S(352, "s390_pci_mmio_write") +_S(353, "s390_pci_mmio_read") +_S(354, "execveat") diff --git a/framework/src/audit/lib/s390x_table.h b/framework/src/audit/lib/s390x_table.h new file mode 100644 index 00000000..73ba0ec0 --- /dev/null +++ b/framework/src/audit/lib/s390x_table.h @@ -0,0 +1,319 @@ +/* s390x_table.h -- + * Copyright 2005-06,2008-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(1, "exit") +_S(2, "fork") +_S(3, "read") +_S(4, "write") +_S(5, "open") +_S(6, "close") +_S(8, "creat") +_S(9, "link") +_S(10, "unlink") +_S(11, "execve") +_S(12, "chdir") +_S(14, "mknod") +_S(15, "chmod") +_S(19, "lseek") +_S(20, "getpid") +_S(21, "mount") +_S(22, "umount") +_S(26, "ptrace") +_S(27, "alarm") +_S(29, "pause") +_S(30, "utime") +_S(33, "access") +_S(34, "nice") +_S(36, "sync") +_S(37, "kill") +_S(38, "rename") +_S(39, "mkdir") +_S(40, "rmdir") +_S(41, "dup") +_S(42, "pipe") +_S(43, "times") +_S(45, "brk") +_S(48, "signal") +_S(51, "acct") +_S(52, "umount2") +_S(54, "ioctl") +_S(55, "fcntl") +_S(57, "setpgid") +_S(60, "umask") +_S(61, "chroot") +_S(62, "ustat") +_S(63, "dup2") +_S(64, "getppid") +_S(65, "getpgrp") +_S(66, "setsid") +_S(67, "sigaction") +_S(72, "sigsuspend") +_S(73, "sigpending") +_S(74, "sethostname") +_S(75, "setrlimit") +_S(77, "getrusage") +_S(78, "gettimeofday") +_S(79, "settimeofday") +_S(83, "symlink") +_S(85, "readlink") +_S(86, "uselib") +_S(87, "swapon") +_S(88, "reboot") +_S(89, "readdir") +_S(90, "mmap") +_S(91, "munmap") +_S(92, "truncate") +_S(93, "ftruncate") +_S(94, "fchmod") +_S(96, "getpriority") +_S(97, "setpriority") +_S(99, "statfs") +_S(100, "fstatfs") +_S(102, "socketcall") +_S(103, "syslog") +_S(104, "setitimer") +_S(105, "getitimer") +_S(106, "stat") +_S(107, "lstat") +_S(108, "fstat") +_S(111, "vhangup") +_S(112, "idle") +_S(114, "wait4") +_S(115, "swapoff") +_S(116, "sysinfo") +_S(117, "ipc") +_S(118, "fsync") +_S(119, "sigreturn") +_S(120, "clone") +_S(121, "setdomainname") +_S(122, "uname") +_S(124, "adjtimex") +_S(125, "mprotect") +_S(126, "sigprocmask") +_S(127, "create_module") +_S(128, "init_module") +_S(129, "delete_module") +_S(130, "get_kernel_syms") +_S(131, "quotactl") +_S(132, "getpgid") +_S(133, "fchdir") +_S(134, "bdflush") +_S(135, "sysfs") +_S(136, "personality") +_S(137, "afs_syscall") +_S(141, "getdents") +_S(142, "select") +_S(143, "flock") +_S(144, "msync") +_S(145, "readv") +_S(146, "writev") +_S(147, "getsid") +_S(148, "fdatasync") +_S(149, "_sysctl") +_S(150, "mlock") +_S(151, "munlock") +_S(152, "mlockall") +_S(153, "munlockall") +_S(154, "sched_setparam") +_S(155, "sched_getparam") +_S(156, "sched_setscheduler") +_S(157, "sched_getscheduler") +_S(158, "sched_yield") +_S(159, "sched_get_priority_max") +_S(160, "sched_get_priority_min") +_S(161, "sched_rr_get_interval") +_S(162, "nanosleep") +_S(163, "mremap") +_S(167, "query_module") +_S(168, "poll") +_S(169, "nfsservctl") +_S(172, "prctl") +_S(173, "rt_sigreturn") +_S(174, "rt_sigaction") +_S(175, "rt_sigprocmask") +_S(176, "rt_sigpending") +_S(177, "rt_sigtimedwait") +_S(178, "rt_sigqueueinfo") +_S(179, "rt_sigsuspend") +_S(180, "pread") +_S(181, "pwrite") +_S(183, "getcwd") +_S(184, "capget") +_S(185, "capset") +_S(186, "sigaltstack") +_S(187, "sendfile") +_S(188, "getpmsg") +_S(189, "putpmsg") +_S(190, "vfork") +_S(191, "getrlimit") +_S(198, "lchown") +_S(199, "getuid") +_S(200, "getgid") +_S(201, "geteuid") +_S(202, "getegid") +_S(203, "setreuid") +_S(204, "setregid") +_S(205, "getgroups") +_S(206, "setgroups") +_S(207, "fchown") +_S(208, "setresuid") +_S(209, "getresuid") +_S(210, "setresgid") +_S(211, "getresgid") +_S(212, "chown") +_S(213, "setuid") +_S(214, "setgid") +_S(215, "setfsuid") +_S(216, "setfsgid") +_S(217, "pivot_root") +_S(218, "mincore") +_S(219, "madvise") +_S(222, "readahead") +_S(224, "setxattr") +_S(225, "lsetxattr") +_S(226, "fsetxattr") +_S(227, "getxattr") +_S(228, "lgetxattr") +_S(229, "fgetxattr") +_S(230, "listxattr") +_S(231, "llistxattr") +_S(232, "flistxattr") +_S(233, "removexattr") +_S(234, "lremovexattr") +_S(235, "fremovexattr") +_S(236, "gettid") +_S(237, "tkill") +_S(238, "futex") +_S(239, "sched_setaffinity") +_S(240, "sched_getaffinity") +_S(241, "tgkill") +_S(243, "io_setup") +_S(244, "io_destroy") +_S(245, "io_getevents") +_S(246, "io_submit") +_S(247, "io_cancel") +_S(248, "exit_group") +_S(249, "epoll_create") +_S(250, "epoll_ctl") +_S(251, "epoll_wait") +_S(252, "set_tid_address") +_S(253, "fadvise64") +_S(254, "timer_create") +_S(255, "timer_settime") +_S(256, "timer_gettime") +_S(257, "timer_getoverrun") +_S(258, "timer_delete") +_S(259, "clock_settime") +_S(260, "clock_gettime") +_S(261, "clock_getres") +_S(262, "clock_nanosleep") +_S(265, "statfs64") +_S(266, "fstatfs64") +_S(267, "remap_file_pages") +//_S(268, "") +//_S(269, "") +//_S(270, "") +_S(271, "mq_open") +_S(272, "mq_unlink") +_S(273, "mq_timedsend") +_S(274, "mq_timedreceive") +_S(275, "mq_notify") +_S(276, "mq_getsetattr") +_S(277, "kexec_load") +_S(278, "add_key") +_S(279, "request_key") +_S(280, "keyctl") +_S(281, "waitid") +_S(282, "ioprio_set") +_S(283, "ioprio_get") +_S(284, "inotify_init") +_S(285, "inotify_add_watch") +_S(286, "inotify_rm_watch") +//_S(287, "") +_S(288, "openat") +_S(289, "mkdirat") +_S(290, "mknodat") +_S(291, "fchownat") +_S(292, "futimesat") +_S(293, "newfstatat") +_S(294, "unlinkat") +_S(295, "renameat") +_S(296, "linkat") +_S(297, "symlinkat") +_S(298, "readlinkat") +_S(299, "fchmodat") +_S(300, "faccessat") +_S(301, "pselect6") +_S(302, "ppoll") +_S(303, "unshare") +_S(304, "set_robust_list") +_S(305, "get_robust_list") +_S(306, "splice") +_S(307, "sync_file_range") +_S(308, "tee") +_S(309, "vmsplice") +//_S(310, "") +_S(311, "getcpu") +_S(312, "epoll_pwait") +_S(313, "utimes") +_S(314, "fallocate") +_S(315, "utimensat") +_S(316, "signalfd") +_S(317, "timerfd") +_S(318, "eventfd") +_S(319, "timerfd_create") +_S(320, "timerfd_settime") +_S(321, "timerfd_gettime") +_S(322, "signalfd4") +_S(323, "eventfd2") +_S(324, "inotify_init1") +_S(325, "pipe2") +_S(326, "dup3") +_S(327, "epoll_create1") +_S(328, "preadv") +_S(329, "pwritev") +_S(330, "rt_tgsigqueueinfo") +_S(331, "perf_event_open") +_S(332, "fanotify_init") +_S(333, "fanotify_mark") +_S(334, "prlimit64") +_S(335, "name_to_handle_at") +_S(336, "open_by_handle_at") +_S(337, "clock_adjtime") +_S(338, "syncfs") +_S(339, "setns") +_S(340, "process_vm_readv") +_S(341, "process_vm_writev") +_S(342, "s390_runtime_instr") +_S(343, "kcmp") +_S(344, "finit_module") +_S(345, "sched_setattr") +_S(346, "sched_getattr") +_S(347, "renameat2") +_S(348, "seccomp") +_S(349, "getrandom") +_S(350, "memfd_create") +_S(351, "bpf") +_S(352, "s390_pci_mmio_write") +_S(353, "s390_pci_mmio_read") +_S(354, "execveat") + diff --git a/framework/src/audit/lib/strsplit.c b/framework/src/audit/lib/strsplit.c new file mode 100644 index 00000000..8337e6da --- /dev/null +++ b/framework/src/audit/lib/strsplit.c @@ -0,0 +1,91 @@ +/* strsplit.c -- + * Copyright 2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include +#include "libaudit.h" +#include "private.h" + +char *audit_strsplit_r(char *s, char **savedpp) +{ + char *ptr; + + if (s) + *savedpp = s; + else { + if (*savedpp == NULL) + return NULL; + *savedpp += 1; + } +retry: + ptr = strchr(*savedpp, ' '); + if (ptr) { + if (ptr == *savedpp) { + *savedpp += 1; + goto retry; + } + s = *savedpp; + *ptr = 0; + *savedpp = ptr; + return s; + } else { + s = *savedpp; + *savedpp = NULL; + if (*s == 0) + return NULL; + return s; + } +} +hidden_def(audit_strsplit_r) + +char *audit_strsplit(char *s) +{ + static char *str = NULL; + char *ptr; + + if (s) + str = s; + else { + if (str == NULL) + return NULL; + str++; + } +retry: + ptr = strchr(str, ' '); + if (ptr) { + if (ptr == str) { + str++; + goto retry; + } + s = str; + *ptr = 0; + str = ptr; + return s; + } else { + s = str; + str = NULL; + if (*s == 0) + return NULL; + return s; + } +} +hidden_def(audit_strsplit) diff --git a/framework/src/audit/lib/syscall-update.txt b/framework/src/audit/lib/syscall-update.txt new file mode 100644 index 00000000..89d63717 --- /dev/null +++ b/framework/src/audit/lib/syscall-update.txt @@ -0,0 +1,20 @@ +The place where syscall information is gathered is: + +arch/alpha/include/uapi/asm/unistd.h +arch/arm/include/uapi/asm/unistd.h +arch/ia64/include/uapi/asm/unistd.h +arch/powerpc/include/uapi/asm/unistd.h +arch/s390/include/uapi/asm/unistd.h +arch/x86/syscalls/syscall_32.tbl +arch/x86/syscalls/syscall_64.tbl +include/uapi/asm-generic/unistd.h (aarch64) + +For src/ausearch-lookup.c: +Inspect include/linux/net.h for socketcall updates +Inspect include/linux/ipc.h for ipccall updates + +For adding new arches, the following might be useful to get a first pass file: + +cat unistd.h | grep '^#define __NR_' | tr -d ')' | tr 'NR+' ' ' | awk '{ printf "_S(%s, \"%s\")\n", $6, $3 }; ' + +it will still need hand editing diff --git a/framework/src/audit/lib/test/Makefile.am b/framework/src/audit/lib/test/Makefile.am new file mode 100644 index 00000000..706fd051 --- /dev/null +++ b/framework/src/audit/lib/test/Makefile.am @@ -0,0 +1,25 @@ +# Copyright 2008 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Miloslav Trmač +# + +check_PROGRAMS = lookup_test +TESTS = $(check_PROGRAMS) + +lookup_test_LDADD = ${top_builddir}/lib/libaudit.la diff --git a/framework/src/audit/lib/test/lookup_test.c b/framework/src/audit/lib/test/lookup_test.c new file mode 100644 index 00000000..5cd00429 --- /dev/null +++ b/framework/src/audit/lib/test/lookup_test.c @@ -0,0 +1,430 @@ +/* lookup_test.c -- A test of table lookups. + * Copyright 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Miloslav Trmač + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include "../libaudit.h" + +/* Number of lookups of random strings */ +#define RAND_ITERATIONS 1000 + +/* Maximum size of randomly generated strings, including the terminating NUL. */ +#define S_LEN 8 + +struct entry { + int val; + const char *s; +}; +#define _S(V, S) { (V), (S) }, + +/* Generate a random string into DEST[S_LEN]. */ +static void +gen_id(char *dest) +{ + size_t i, len; + + assert(S_LEN >= 2); + len = 1 + rand() % (S_LEN - 1); + assert('A' == 0x41 && 'a' == 0x61); /* ASCII */ + for (i = 0; i < len; i++) { + /* Don't start with a digit, audit_name_to_msg_type() interprets + those strings specially. */ + do { + dest[i] = 0x21 + rand() % (0x7F - 0x21); + } while (i == 0 && dest[i] >= '0' && dest[i] <= '9'); + } + dest[i] = '\0'; +} + +#define TEST_I2S(EXCL) \ + do { \ + size_t i; \ + \ + for (i = 0; i < sizeof(t) / sizeof(*t); i++) { \ + const char *s; \ + \ + if (EXCL) \ + continue; \ + s = I2S(t[i].val); \ + if (s == NULL) { \ + fprintf(stderr, \ + "%d -> `%s' not found\n", \ + t[i].val, t[i].s); \ + abort(); \ + } \ + if (strcmp(t[i].s, s) != 0) { \ + fprintf(stderr, \ + "%d -> `%s' mismatch `%s'\n", \ + t[i].val, t[i].s, s); \ + abort(); \ + } \ + } \ + for (i = 0; i < RAND_ITERATIONS; i++) { \ + int val; \ + size_t j; \ + val = rand(); \ + for (j = 0; j < sizeof(t) / sizeof(*t); j++) { \ + if (t[j].val == val) \ + goto test_i2s_found; \ + } \ + assert(I2S(val) == NULL); \ + test_i2s_found: \ + ; \ + } \ + } while (0) + +#define TEST_S2I(ERR_VALUE) \ + do { \ + size_t i; \ + char buf[S_LEN]; \ + \ + for (i = 0; i < sizeof(t) / sizeof(*t); i++) \ + assert(S2I(t[i].s) == t[i].val); \ + for (i = 0; i < RAND_ITERATIONS; i++) { \ + /* Blindly assuming this will not generate a \ + meaningful identifier. */ \ + gen_id(buf); \ + if (S2I(buf) != (ERR_VALUE)) { \ + fprintf(stderr, \ + "Unexpected match `%s'\n", \ + buf); \ + abort(); \ + } \ + } \ + } while (0) + +#ifdef WITH_ALPHA +static void +test_alpha_table(void) +{ + static const struct entry t[] = { +#include "../alpha_table.h" + }; + + printf("Testing alpha_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_ALPHA) +#define S2I(S) audit_name_to_syscall((S), MACH_ALPHA) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} +#endif + +#ifdef WITH_ARM +static void +test_arm_table(void) +{ + static const struct entry t[] = { +#include "../arm_table.h" + }; + + printf("Testing arm_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_ARM) +#define S2I(S) audit_name_to_syscall((S), MACH_ARM) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} +#endif + +#ifdef WITH_AARCH64 +static void +test_aarch64_table(void) +{ + static const struct entry t[] = { +#include "../aarch64_table.h" + }; + + printf("Testing aarch64_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_AARCH64) +#define S2I(S) audit_name_to_syscall((S), MACH_AARCH64) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} +#endif + +static void +test_i386_table(void) +{ + static const struct entry t[] = { +#include "../i386_table.h" + }; + + printf("Testing i386_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_X86) +#define S2I(S) audit_name_to_syscall((S), MACH_X86) + TEST_I2S(strcmp(t[i].s, "madvise1") == 0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_ia64_table(void) +{ + static const struct entry t[] = { +#include "../ia64_table.h" + }; + + printf("Testing ia64_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_IA64) +#define S2I(S) audit_name_to_syscall((S), MACH_IA64) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_ppc_table(void) +{ + static const struct entry t[] = { +#include "../ppc_table.h" + }; + + printf("Testing ppc_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_PPC) +#define S2I(S) audit_name_to_syscall((S), MACH_PPC) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_s390_table(void) +{ + static const struct entry t[] = { +#include "../s390_table.h" + }; + + printf("Testing s390_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_S390) +#define S2I(S) audit_name_to_syscall((S), MACH_S390) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_s390x_table(void) +{ + static const struct entry t[] = { +#include "../s390x_table.h" + }; + + printf("Testing s390x_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_S390X) +#define S2I(S) audit_name_to_syscall((S), MACH_S390X) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_x86_64_table(void) +{ + static const struct entry t[] = { +#include "../x86_64_table.h" + }; + + printf("Testing x86_64_table...\n"); +#define I2S(I) audit_syscall_to_name((I), MACH_86_64) +#define S2I(S) audit_name_to_syscall((S), MACH_86_64) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_actiontab(void) +{ + static const struct entry t[] = { +#include "../actiontab.h" + }; + + printf("Testing actiontab...\n"); +#define I2S(I) audit_action_to_name(I) +#define S2I(S) audit_name_to_action(S) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_errtab(void) +{ + static const struct entry t[] = { +#include "../errtab.h" + }; + + printf("Testing errtab...\n"); +#define I2S(I) audit_errno_to_name(I) +#define S2I(S) audit_name_to_errno(S) + TEST_I2S(strcmp(t[i].s, "EWOULDBLOCK") == 0 + || strcmp(t[i].s, "EDEADLOCK") == 0); + TEST_S2I(0); +#undef I2S +#undef S2I +} + +static void +test_fieldtab(void) +{ + static const struct entry t[] = { +#include "../fieldtab.h" + }; + + printf("Testing fieldtab...\n"); +#define I2S(I) audit_field_to_name(I) +#define S2I(S) audit_name_to_field(S) + TEST_I2S(strcmp(t[i].s, "loginuid") == 0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_flagtab(void) +{ + static const struct entry t[] = { +#include "../flagtab.h" + }; + + printf("Testing flagtab...\n"); +#define I2S(I) audit_flag_to_name(I) +#define S2I(S) audit_name_to_flag(S) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_ftypetab(void) +{ + static const struct entry t[] = { +#include "../ftypetab.h" + }; + + printf("Testing ftypetab...\n"); +#define I2S(I) audit_ftype_to_name(I) +#define S2I(S) audit_name_to_ftype(S) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_machinetab(void) +{ + static const struct entry t[] = { +#include "../machinetab.h" + }; + + printf("Testing machinetab...\n"); +#define I2S(I) audit_machine_to_name(I) +#define S2I(S) audit_name_to_machine(S) + TEST_I2S((t[i].s[0] == 'i' && t[i].s[1] >= '4' && t[i].s[1] <= '6' + && strcmp(t[i].s + 2, "86") == 0) || + (strncmp(t[i].s, "arm", 3) == 0)); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_msg_typetab(void) +{ + static const struct entry t[] = { +#include "../msg_typetab.h" + }; + + printf("Testing msg_typetab...\n"); +#define I2S(I) audit_msg_type_to_name(I) +#define S2I(S) audit_name_to_msg_type(S) + TEST_I2S(0); + TEST_S2I(-1); +#undef I2S +#undef S2I +} + +static void +test_optab(void) +{ + static const struct entry t[] = { +#include "../optab.h" + }; + + printf("Testing optab...\n"); +#define I2S(I) audit_operator_to_symbol(I) + TEST_I2S(0); +#undef I2S +} + +int +main(void) +{ + // This is only for preventing collisions in s2i tests. + // If collisions are found in future, change the number. + srand(3); +#ifdef WITH_ALPHA + test_alpha_table(); +#endif +#ifdef WITH_ARM + test_arm_table(); +#endif +#ifdef WITH_AARCH64 + test_aarch64_table(); +#endif + test_i386_table(); + test_ia64_table(); + test_ppc_table(); + test_s390_table(); + test_s390x_table(); + test_x86_64_table(); + test_actiontab(); + test_errtab(); + test_fieldtab(); + test_flagtab(); + test_ftypetab(); + test_machinetab(); + test_msg_typetab(); + test_optab(); + return EXIT_SUCCESS; +} + diff --git a/framework/src/audit/lib/x86_64_table.h b/framework/src/audit/lib/x86_64_table.h new file mode 100644 index 00000000..0c8d41ad --- /dev/null +++ b/framework/src/audit/lib/x86_64_table.h @@ -0,0 +1,345 @@ +/* x86_64_table.h -- + * Copyright 2005-15 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +_S(0, "read") +_S(1, "write") +_S(2, "open") +_S(3, "close") +_S(4, "stat") +_S(5, "fstat") +_S(6, "lstat") +_S(7, "poll") +_S(8, "lseek") +_S(9, "mmap") +_S(10, "mprotect") +_S(11, "munmap") +_S(12, "brk") +_S(13, "rt_sigaction") +_S(14, "rt_sigprocmask") +_S(15, "rt_sigreturn") +_S(16, "ioctl") +_S(17, "pread") +_S(18, "pwrite") +_S(19, "readv") +_S(20, "writev") +_S(21, "access") +_S(22, "pipe") +_S(23, "select") +_S(24, "sched_yield") +_S(25, "mremap") +_S(26, "msync") +_S(27, "mincore") +_S(28, "madvise") +_S(29, "shmget") +_S(30, "shmat") +_S(31, "shmctl") +_S(32, "dup") +_S(33, "dup2") +_S(34, "pause") +_S(35, "nanosleep") +_S(36, "getitimer") +_S(37, "alarm") +_S(38, "setitimer") +_S(39, "getpid") +_S(40, "sendfile") +_S(41, "socket") +_S(42, "connect") +_S(43, "accept") +_S(44, "sendto") +_S(45, "recvfrom") +_S(46, "sendmsg") +_S(47, "recvmsg") +_S(48, "shutdown") +_S(49, "bind") +_S(50, "listen") +_S(51, "getsockname") +_S(52, "getpeername") +_S(53, "socketpair") +_S(54, "setsockopt") +_S(55, "getsockopt") +_S(56, "clone") +_S(57, "fork") +_S(58, "vfork") +_S(59, "execve") +_S(60, "exit") +_S(61, "wait4") +_S(62, "kill") +_S(63, "uname") +_S(64, "semget") +_S(65, "semop") +_S(66, "semctl") +_S(67, "shmdt") +_S(68, "msgget") +_S(69, "msgsnd") +_S(70, "msgrcv") +_S(71, "msgctl") +_S(72, "fcntl") +_S(73, "flock") +_S(74, "fsync") +_S(75, "fdatasync") +_S(76, "truncate") +_S(77, "ftruncate") +_S(78, "getdents") +_S(79, "getcwd") +_S(80, "chdir") +_S(81, "fchdir") +_S(82, "rename") +_S(83, "mkdir") +_S(84, "rmdir") +_S(85, "creat") +_S(86, "link") +_S(87, "unlink") +_S(88, "symlink") +_S(89, "readlink") +_S(90, "chmod") +_S(91, "fchmod") +_S(92, "chown") +_S(93, "fchown") +_S(94, "lchown") +_S(95, "umask") +_S(96, "gettimeofday") +_S(97, "getrlimit") +_S(98, "getrusage") +_S(99, "sysinfo") +_S(100, "times") +_S(101, "ptrace") +_S(102, "getuid") +_S(103, "syslog") +_S(104, "getgid") +_S(105, "setuid") +_S(106, "setgid") +_S(107, "geteuid") +_S(108, "getegid") +_S(109, "setpgid") +_S(110, "getppid") +_S(111, "getpgrp") +_S(112, "setsid") +_S(113, "setreuid") +_S(114, "setregid") +_S(115, "getgroups") +_S(116, "setgroups") +_S(117, "setresuid") +_S(118, "getresuid") +_S(119, "setresgid") +_S(120, "getresgid") +_S(121, "getpgid") +_S(122, "setfsuid") +_S(123, "setfsgid") +_S(124, "getsid") +_S(125, "capget") +_S(126, "capset") +_S(127, "rt_sigpending") +_S(128, "rt_sigtimedwait") +_S(129, "rt_sigqueueinfo") +_S(130, "rt_sigsuspend") +_S(131, "sigaltstack") +_S(132, "utime") +_S(133, "mknod") +_S(134, "uselib") +_S(135, "personality") +_S(136, "ustat") +_S(137, "statfs") +_S(138, "fstatfs") +_S(139, "sysfs") +_S(140, "getpriority") +_S(141, "setpriority") +_S(142, "sched_setparam") +_S(143, "sched_getparam") +_S(144, "sched_setscheduler") +_S(145, "sched_getscheduler") +_S(146, "sched_get_priority_max") +_S(147, "sched_get_priority_min") +_S(148, "sched_rr_get_interval") +_S(149, "mlock") +_S(150, "munlock") +_S(151, "mlockall") +_S(152, "munlockall") +_S(153, "vhangup") +_S(154, "modify_ldt") +_S(155, "pivot_root") +_S(156, "_sysctl") +_S(157, "prctl") +_S(158, "arch_prctl") +_S(159, "adjtimex") +_S(160, "setrlimit") +_S(161, "chroot") +_S(162, "sync") +_S(163, "acct") +_S(164, "settimeofday") +_S(165, "mount") +_S(166, "umount2") +_S(167, "swapon") +_S(168, "swapoff") +_S(169, "reboot") +_S(170, "sethostname") +_S(171, "setdomainname") +_S(172, "iopl") +_S(173, "ioperm") +_S(174, "create_module") +_S(175, "init_module") +_S(176, "delete_module") +_S(177, "get_kernel_syms") +_S(178, "query_module") +_S(179, "quotactl") +_S(180, "nfsservctl") +_S(181, "getpmsg") +_S(182, "putpmsg") +_S(183, "afs_syscall") +_S(184, "tuxcall") +_S(185, "security") +_S(186, "gettid") +_S(187, "readahead") +_S(188, "setxattr") +_S(189, "lsetxattr") +_S(190, "fsetxattr") +_S(191, "getxattr") +_S(192, "lgetxattr") +_S(193, "fgetxattr") +_S(194, "listxattr") +_S(195, "llistxattr") +_S(196, "flistxattr") +_S(197, "removexattr") +_S(198, "lremovexattr") +_S(199, "fremovexattr") +_S(200, "tkill") +_S(201, "time") +_S(202, "futex") +_S(203, "sched_setaffinity") +_S(204, "sched_getaffinity") +_S(205, "set_thread_area") +_S(206, "io_setup") +_S(207, "io_destroy") +_S(208, "io_getevents") +_S(209, "io_submit") +_S(210, "io_cancel") +_S(211, "get_thread_area") +_S(212, "lookup_dcookie") +_S(213, "epoll_create") +_S(214, "epoll_ctl_old") +_S(215, "epoll_wait_old") +_S(216, "remap_file_pages") +_S(217, "getdents64") +_S(218, "set_tid_address") +_S(219, "restart_syscall") +_S(220, "semtimedop") +_S(221, "fadvise64") +_S(222, "timer_create") +_S(223, "timer_settime") +_S(224, "timer_gettime") +_S(225, "timer_getoverrun") +_S(226, "timer_delete") +_S(227, "clock_settime") +_S(228, "clock_gettime") +_S(229, "clock_getres") +_S(230, "clock_nanosleep") +_S(231, "exit_group") +_S(232, "epoll_wait") +_S(233, "epoll_ctl") +_S(234, "tgkill") +_S(235, "utimes") +_S(236, "vserver") +_S(237, "mbind") +_S(238, "set_mempolicy") +_S(239, "get_mempolicy") +_S(240, "mq_open") +_S(241, "mq_unlink") +_S(242, "mq_timedsend") +_S(243, "mq_timedreceive") +_S(244, "mq_notify") +_S(245, "mq_getsetattr") +_S(246, "kexec_load") +_S(247, "waitid") +_S(248, "add_key") +_S(249, "request_key") +_S(250, "keyctl") +_S(251, "ioprio_set") +_S(252, "ioprio_get") +_S(253, "inotify_init") +_S(254, "inotify_add_watch") +_S(255, "inotify_rm_watch") +_S(256, "migrate_pages") +_S(257, "openat") +_S(258, "mkdirat") +_S(259, "mknodat") +_S(260, "fchownat") +_S(261, "futimesat") +_S(262, "newfstatat") +_S(263, "unlinkat") +_S(264, "renameat") +_S(265, "linkat") +_S(266, "symlinkat") +_S(267, "readlinkat") +_S(268, "fchmodat") +_S(269, "faccessat") +_S(270, "pselect6") +_S(271, "ppoll") +_S(272, "unshare") +_S(273, "set_robust_list") +_S(274, "get_robust_list") +_S(275, "splice") +_S(276, "tee") +_S(277, "sync_file_range") +_S(278, "vmsplice") +_S(279, "move_pages") +_S(280, "utimensat") +_S(281, "epoll_pwait") +_S(282, "signalfd") +_S(283, "timerfd") +_S(284, "eventfd") +_S(285, "fallocate") +_S(286, "timerfd_settime") +_S(287, "timerfd_gettime") +_S(288, "accept4") +_S(289, "signalfd4") +_S(290, "eventfd2") +_S(291, "epoll_create1") +_S(292, "dup3") +_S(293, "pipe2") +_S(294, "inotify_init1") +_S(295, "preadv") +_S(296, "pwritev") +_S(297, "rt_tgsigqueueinfo") +_S(298, "perf_event_open") +_S(299, "recvmmsg") +_S(300, "fanotify_init") +_S(301, "fanotify_mark") +_S(302, "prlimit64") +_S(303, "name_to_handle_at") +_S(304, "open_by_handle_at") +_S(305, "clock_adjtime") +_S(306, "syncfs") +_S(307, "sendmmsg") +_S(308, "setns") +_S(309, "getcpu") +_S(310, "process_vm_readv") +_S(311, "process_vm_writev") +_S(312, "kcmp") +_S(313, "finit_module") +_S(314, "sched_setattr") +_S(315, "sched_getattr") +_S(316, "renameat2") +_S(317, "seccomp") +_S(318, "getrandom") +_S(319, "memfd_create") +_S(320, "kexec_file_load") +_S(321, "bpf") +_S(322, "execveat") diff --git a/framework/src/audit/ltmain.sh b/framework/src/audit/ltmain.sh new file mode 100644 index 00000000..d70dc75c --- /dev/null +++ b/framework/src/audit/ltmain.sh @@ -0,0 +1,6909 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun configure. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +basename="s,^.*/,,g" + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + +# The name of this program: +progname=`echo "$progpath" | $SED $basename` +modename="$progname" + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION=1.5.22 +TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" + +# Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE). +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +duplicate_deps=no +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +##################################### +# Shell function definitions: +# This seems to be the best place for them + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $mkdir "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || { + $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 + exit $EXIT_FAILURE + } + fi + + $echo "X$my_tmpdir" | $Xsed +} + + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () +{ + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ + $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | \ + $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $echo $win32_libid_type +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case "$@ " in + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit $EXIT_FAILURE +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + + $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" + $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 + exit $EXIT_FAILURE + fi +} + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + my_status="" + + $show "${rm}r $my_gentop" + $run ${rm}r "$my_gentop" + $show "$mkdir $my_gentop" + $run $mkdir "$my_gentop" + my_status=$? + if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then + exit $my_status + fi + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + extracted_serial=`expr $extracted_serial + 1` + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + $show "${rm}r $my_xdir" + $run ${rm}r "$my_xdir" + $show "$mkdir $my_xdir" + $run $mkdir "$my_xdir" + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then + exit $exit_status + fi + case $host in + *-darwin*) + $show "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + if test -z "$run"; then + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` + darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` + if test -n "$darwin_arches"; then + darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + $show "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we have a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + lipo -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + ${rm}r unfat-$$ + cd "$darwin_orig_dir" + else + cd "$darwin_orig_dir" + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + fi # $run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + func_extract_archives_result="$my_oldobjs" +} +# End of Shell function definitions +##################################### + +# Darwin sucks +eval std_shrext=\"$shrext_cmds\" + +disable_libs=no + +# Parse our command line options once, thoroughly. +while test "$#" -gt 0 +do + arg="$1" + shift + + case $arg in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + execute_dlfiles) + execute_dlfiles="$execute_dlfiles $arg" + ;; + tag) + tagname="$arg" + preserve_args="${preserve_args}=$arg" + + # Check whether tagname contains only valid characters + case $tagname in + *[!-_A-Za-z0-9,/]*) + $echo "$progname: invalid tag name: $tagname" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $tagname in + CC) + # Don't test for the "default" C tag, as we know, it's there, but + # not specially marked. + ;; + *) + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then + taglist="$taglist $tagname" + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" + else + $echo "$progname: ignoring unknown tag $tagname" 1>&2 + fi + ;; + esac + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case $arg in + --help) + show_help=yes + ;; + + --version) + $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + $echo + $echo "Copyright (C) 2005 Free Software Foundation, Inc." + $echo "This is free software; see the source for copying conditions. There is NO" + $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit $? + ;; + + --config) + ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath + # Now print the configurations for the tags. + for tagname in $taglist; do + ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" + done + exit $? + ;; + + --debug) + $echo "$progname: enabling shell trace mode" + set -x + preserve_args="$preserve_args $arg" + ;; + + --dry-run | -n) + run=: + ;; + + --features) + $echo "host: $host" + if test "$build_libtool_libs" = yes; then + $echo "enable shared libraries" + else + $echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + $echo "enable static libraries" + else + $echo "disable static libraries" + fi + exit $? + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --preserve-dup-deps) duplicate_deps="yes" ;; + + --quiet | --silent) + show=: + preserve_args="$preserve_args $arg" + ;; + + --tag) + prevopt="--tag" + prev=tag + preserve_args="$preserve_args --tag" + ;; + --tag=*) + set tag "$optarg" ${1+"$@"} + shift + prev=tag + preserve_args="$preserve_args --tag" + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE +fi + +case $disable_libs in +no) + ;; +shared) + build_libtool_libs=no + build_old_libs=yes + ;; +static) + build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` + ;; +esac + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 + $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 + case $nonopt in + *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) + mode=link + for arg + do + case $arg in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case $mode in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + if test -n "$libobj" ; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit $EXIT_FAILURE + fi + arg_mode=target + continue + ;; + + -static | -prefer-pic | -prefer-non-pic) + later="$later $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + lastarg="$lastarg $arg" + done + IFS="$save_ifs" + lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` + + # Add the arguments to base_compile. + base_compile="$base_compile $lastarg" + continue + ;; + + * ) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + case $lastarg in + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, and some SunOS ksh mistreat backslash-escaping + # in scan sets (worked around with variable expansion), + # and furthermore cannot handle '|' '&' '(' ')' in scan sets + # at all, so we specify them separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + lastarg="\"$lastarg\"" + ;; + esac + + base_compile="$base_compile $lastarg" + done # for arg + + case $arg_mode in + arg) + $echo "$modename: you must specify an argument for -Xcompile" + exit $EXIT_FAILURE + ;; + target) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit $EXIT_FAILURE + ;; + *) + # Get the name of the library object. + [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSifmso]' + case $libobj in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.ii) xform=ii ;; + *.class) xform=class ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + *.java) xform=java ;; + *.obj) xform=obj ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case $libobj in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -static) + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` + case $qlibobj in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qlibobj="\"$qlibobj\"" ;; + esac + test "X$libobj" != "X$qlibobj" \ + && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." + objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir= + else + xdir=$xdir/ + fi + lobj=${xdir}$objdir/$objname + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $run ln "$progpath" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + $echo "$srcfile" > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` + case $qsrcfile in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qsrcfile="\"$qsrcfile\"" ;; + esac + + $run $rm "$libobj" "${libobj}T" + + # Create a libtool object file (analogous to a ".la" file), + # but don't create it if we're doing a dry run. + test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + $show "$mv $output_obj $lobj" + if $run $mv $output_obj $lobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the PIC object to the libtool object file. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test + ;; + *) qarg=$arg ;; + esac + libtool_args="$libtool_args $qarg" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit $EXIT_FAILURE + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat $save_arg` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + done + else + $echo "$modename: link input file \`$save_arg' does not exist" + exit $EXIT_FAILURE + fi + arg=$save_arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + compile_command="$compile_command $wl$qarg" + finalize_command="$finalize_command $wl$qarg" + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + darwin_framework|darwin_framework_skip) + test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + prev= + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: more than one -exported-symbols argument is not allowed" + exit $EXIT_FAILURE + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework|-arch|-isysroot) + case " $CC " in + *" ${arg} ${1} "* | *" ${arg} ${1} "*) + prev=darwin_framework_skip ;; + *) compiler_flags="$compiler_flags $arg" + prev=darwin_framework ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + ;; + esac + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + notinst_path="$notinst_path $dir" + fi + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs -framework System" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + -model) + compile_command="$compile_command $arg" + compiler_flags="$compiler_flags $arg" + finalize_command="$finalize_command $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + compiler_flags="$compiler_flags $arg" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # -64, -mips[0-9] enable 64-bit mode on the SGI compiler + # -r[0-9][0-9]* specifies the processor on the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler + # +DA*, +DD* enable 64-bit mode on the HP compiler + # -q* pass through compiler args for the IBM compiler + # -m* pass through architecture-specific compiler args for GCC + # -m*, -t[45]*, -txscale* pass through architecture-specific + # compiler args for GCC + # -pg pass through profiling flag for GCC + # @file GCC response files + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ + -t[45]*|-txscale*|@*) + + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + compiler_flags="$compiler_flags $arg" + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # The PATH hackery in wrapper scripts is required on Windows + # in order for the loader to find any dlls it needs. + $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 + $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Wl,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $wl$flag" + linker_flags="$linker_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done # argument parsing loop + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + # Create the object directory. + if test ! -d "$output_objdir"; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then + exit $exit_status + fi + fi + + # Determine the type of output + case $output in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + case $host in + *cygwin* | *mingw* | *pw32*) + # don't eliminate duplications in $postdeps and $predeps + duplicate_compiler_generated_deps=yes + ;; + *) + duplicate_compiler_generated_deps=$duplicate_deps + ;; + esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if test "X$duplicate_deps" = "Xyes" ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + case $linkmode in + lib) + passes="conv link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + for pass in $passes; do + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags="$compiler_flags $deplib" + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 + continue + fi + name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` + for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if (${SED} -e '2q' $lib | + grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + library_names= + old_library= + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + *) + $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + if eval $echo \"$deplib\" 2>/dev/null \ + | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $echo + $echo "*** Warning: Trying to link with static lib archive $deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because the file extensions .$libext of this argument makes me believe" + $echo "*** that it is just a static archive that I should not used here." + else + $echo + $echo "*** Warning: Linking the shared library $output against the" + $echo "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + if test "$found" = yes || test -f "$lib"; then : + else + $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 + exit $EXIT_FAILURE + fi + + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + $echo "$modename: \`$lib' is not a convenience library" 1>&2 + exit $EXIT_FAILURE + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + abs_ladir="$ladir" + fi + ;; + esac + laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + $echo "$modename: warning: library \`$lib' was moved." 1>&2 + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *" $absdir "*) ;; + *) temp_rpath="$temp_rpath $absdir" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes ; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + # This is a shared library + + # Warn about portability, can't link against -module's on + # some systems (darwin) + if test "$shouldnotlink" = yes && test "$pass" = link ; then + $echo + if test "$linkmode" = prog; then + $echo "*** Warning: Linking the executable $output against the loadable module" + else + $echo "*** Warning: Linking the shared library $output against the loadable module" + fi + $echo "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + realname="$2" + shift; shift + libname=`eval \\$echo \"$libname_spec\"` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw*) + major=`expr $current - $age` + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + soname=`$echo $soroot | ${SED} -e 's/^.*\///'` + newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + $show "extracting exported symbol list from \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$extract_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + $show "generating import library for \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$old_archive_from_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a module then we can not link against + # it, someone is ignoring the new warnings I added + if /usr/bin/file -L $add 2> /dev/null | + $EGREP ": [^:]* bundle" >/dev/null ; then + $echo "** Warning, lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $echo + $echo "** And there doesn't seem to be a static archive available" + $echo "** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit $EXIT_FAILURE + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $echo + $echo "*** Warning: This system can not link to static lib archive $lib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $echo "*** But as you try to build a module library, libtool will still create " + $echo "*** a static module, that should work as long as the dlopening application" + $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$deplib" && dir="." + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + fi + ;; + esac + if grep "^installed=no" $deplib > /dev/null; then + path="$absdir/$objdir" + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + if test "$absdir" != "$libdir"; then + $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 + fi + path="$absdir" + fi + depdepl= + case $host in + *-*-darwin*) + # we do not want to link against static libs, + # but need to link against shared + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$path/$depdepl" ; then + depdepl="$path/$depdepl" + fi + # do not add paths which are already there + case " $newlib_search_path " in + *" $path "*) ;; + *) newlib_search_path="$newlib_search_path $path";; + esac + fi + path="" + ;; + *) + path="-L$path" + ;; + esac + ;; + -l*) + case $host in + *-*-darwin*) + # Again, we only want to link against shared libraries + eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` + for tmp in $newlib_search_path ; do + if test -f "$tmp/lib$tmp_libs.dylib" ; then + eval depdepl="$tmp/lib$tmp_libs.dylib" + break + fi + done + path="" + ;; + *) continue ;; + esac + ;; + *) continue ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + case " $deplibs " in + *" $depdepl "*) ;; + *) deplibs="$depdepl $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 + fi + + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 + fi + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + if test "$module" = no; then + $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 + exit $EXIT_FAILURE + else + $echo + $echo "*** Warning: Linking the shared library $output against the non-libtool" + $echo "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + if test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 + fi + + set dummy $rpath + if test "$#" -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 + fi + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + IFS="$save_ifs" + + if test -n "$8"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$2" + number_minor="$3" + number_revision="$4" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows|none) + current=`expr $number_major + $number_minor` + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + current=`expr $number_major + $number_minor - 1` + age="$number_minor" + revision="$number_minor" + ;; + esac + ;; + no) + current="$2" + revision="$3" + age="$4" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + $echo "$modename: CURRENT \`$current' must be a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + $echo "$modename: REVISION \`$revision' must be a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + $echo "$modename: AGE \`$age' must be a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + if test "$age" -gt "$current"; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + minor_current=`expr $current + 1` + verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current"; + ;; + + irix | nonstopux) + major=`expr $current - $age + 1` + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + iface=`expr $revision - $loop` + loop=`expr $loop - 1` + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + major=.`expr $current - $age` + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + major=`expr $current - $age` + versuffix="-$major" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + fi + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$echo "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + if test -n "$removelist"; then + $show "${rm}r $removelist" + $run ${rm}r $removelist + fi + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. +# for path in $notinst_path; do +# lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"` +# deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"` +# dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"` +# done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs -framework System" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $rm conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null \ + | grep " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for file magic test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a file magic. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + for a_deplib in $deplibs; do + name=`expr $a_deplib : '-l\(.*\)'` + # If $name is empty we are operating on a -L argument. + if test -n "$name" && test "$name" != "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval \\$echo \"$libname_spec\"` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval $echo \"$potent_lib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a regex pattern. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ + -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` + done + fi + if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ + | grep . >/dev/null; then + $echo + if test "X$deplibs_check_method" = "Xnone"; then + $echo "*** Warning: inter-library dependencies are not supported in this platform." + else + $echo "*** Warning: inter-library dependencies are not known to be supported." + fi + $echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $echo + $echo "*** Warning: libtool could not satisfy all declared inter-library" + $echo "*** dependencies of module $libname. Therefore, libtool will create" + $echo "*** a static module, that should work as long as the dlopening" + $echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $echo "*** The inter-library dependencies that have been dropped here will be" + $echo "*** automatically added whenever a program is linked with this library" + $echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $echo + $echo "*** Since this library must not contain undefined symbols," + $echo "*** because either the platform does not support them or" + $echo "*** it was explicitly requested with -no-undefined," + $echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + deplibs="$new_libs" + + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + if len=`expr "X$cmd" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + $show "$cmd" + $run eval "$cmd" || exit $? + skipped_export=false + else + # The command line is too long to execute in one step. + $show "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex"; then + $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" + $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + $show "$mv \"${export_symbols}T\" \"$export_symbols\"" + $run eval '$mv "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + libobjs="$libobjs $func_extract_archives_result" + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + len=`expr "X$test_cmds" : ".*" 2>/dev/null` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise. + $echo "creating reloadable object files..." + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + output_la=`$echo "X$output" | $Xsed -e "$basename"` + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + delfiles= + last_robj= + k=1 + output=$output_objdir/$output_la-${k}.$objext + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + eval test_cmds=\"$reload_cmds $objlist $last_robj\" + if test "X$objlist" = X || + { len=`expr "X$test_cmds" : ".*" 2>/dev/null` && + test "$len" -le "$max_cmd_len"; }; then + objlist="$objlist $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + k=`expr $k + 1` + output=$output_objdir/$output_la-${k}.$objext + objlist=$obj + len=1 + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + + if ${skipped_export-false}; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + libobjs=$output + # Append the command to create the export file. + eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" + fi + + # Set up a command to remove the reloadable object files + # after they are used. + i=0 + while test "$i" -lt "$k" + do + i=`expr $i + 1` + delfiles="$delfiles $output_objdir/$output_la-${i}.$objext" + done + + $echo "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + + # Append the command to remove the reloadable object files + # to the just-reset $cmds. + eval cmds=\"\$cmds~\$rm $delfiles\" + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 + fi + + case $output in + *.lo) + if test -n "$objs$old_deplibs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit $EXIT_FAILURE + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e "$lo2o"` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$echo "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $run eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; + esac + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 + fi + + if test "$preload" = yes; then + if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && + test "$dlopen_self_static" = unknown; then + $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." + fi + fi + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + case $host in + *darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + if test "$tagname" = CXX ; then + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + fi + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + testbindir=`$echo "X$libdir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + dlsyms= + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${outputname}S.c" + else + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + fi + fi + + if test -n "$dlsyms"; then + case $dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${outputname}.nm" + + $show "$rm $nlist ${nlist}S ${nlist}T" + $run $rm "$nlist" "${nlist}S" "${nlist}T" + + # Parse the name list into a source file. + $show "creating $output_objdir/$dlsyms" + + test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define lt_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + $show "generating symbol list for \`$output'" + + test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for arg in $progfiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + if test -n "$export_symbols_regex"; then + $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $run $rm $export_symbols + $run eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* ) + $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + $run eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + else + $run eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + $run eval 'grep -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + $run eval 'mv "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* ) + $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + $run eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` + $run eval '$echo ": $name " >> "$nlist"' + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -z "$run"; then + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $mv "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if grep -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + grep -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' + else + $echo '/* NONE */' >> "$output_objdir/$dlsyms" + fi + + $echo >> "$output_objdir/$dlsyms" "\ + +#undef lt_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define lt_ptr void * +#else +# define lt_ptr char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +" + + case $host in + *cygwin* | *mingw* ) + $echo >> "$output_objdir/$dlsyms" "\ +/* DATA imports from DLLs on WIN32 can't be const, because + runtime relocations are performed -- see ld's documentation + on pseudo-relocs */ +struct { +" + ;; + * ) + $echo >> "$output_objdir/$dlsyms" "\ +const struct { +" + ;; + esac + + + $echo >> "$output_objdir/$dlsyms" "\ + const char *name; + lt_ptr address; +} +lt_preloaded_symbols[] = +{\ +" + + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" + + $echo >> "$output_objdir/$dlsyms" "\ + {0, (lt_ptr) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + fi + + pic_flag_for_symtable= + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; + esac;; + *-*-hpux*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag";; + esac + esac + + # Now compile the dynamic symbol file. + $show "(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" + $run eval '(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? + + # Clean up the generated files. + $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" + $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" + + # Transform the symbol file into the correct name. + case $host in + *cygwin* | *mingw* ) + if test -f "$output_objdir/${outputname}.def" ; then + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%" | $NL2SP` + else + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` + fi + ;; + * ) + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%" | $NL2SP` + ;; + esac + ;; + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "s% @SYMFILE@%%" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "s% @SYMFILE@%%" | $NL2SP` + fi + + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e 's%@OUTPUT@%'"$output"'%g' | $NL2SP` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + $show "$link_command" + $run eval "$link_command" + exit_status=$? + + # Delete the generated files. + if test -n "$dlsyms"; then + $show "$rm $output_objdir/${outputname}S.${objext}" + $run $rm "$output_objdir/${outputname}S.${objext}" + fi + + exit $exit_status + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $run $rm $output + # Link the executable and exit + $show "$link_command" + $run eval "$link_command" || exit $? + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 + $echo "$modename: \`$output' will be relinked during installation" 1>&2 + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $SP2NL | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g' | $NL2SP` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + + $show "$link_command" + $run eval "$link_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$echo "X$relink_command" | $SP2NL | $Xsed -e "$sed_quote_subst" | $NL2SP` + fi + + # Quote $echo for shipping. + if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then + case $progpath in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; + *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; + esac + qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + output_name=`basename $output` + output_path=`dirname $output` + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $rm $cwrappersource $cwrapper + trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + cat > $cwrappersource <> $cwrappersource<<"EOF" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +/* -DDEBUG is fairly common in CFLAGS. */ +#undef DEBUG +#if defined DEBUGWRAPPER +# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) +#else +# define DEBUG(format, ...) +#endif + +const char *program_name = NULL; + +void * xmalloc (size_t num); +char * xstrdup (const char *string); +const char * base_name (const char *name); +char * find_executable(const char *wrapper); +int check_executable(const char *path); +char * strendzap(char *str, const char *pat); +void lt_fatal (const char *message, ...); + +int +main (int argc, char *argv[]) +{ + char **newargz; + int i; + + program_name = (char *) xstrdup (base_name (argv[0])); + DEBUG("(main) argv[0] : %s\n",argv[0]); + DEBUG("(main) program_name : %s\n",program_name); + newargz = XMALLOC(char *, argc+2); +EOF + + cat >> $cwrappersource <> $cwrappersource <<"EOF" + newargz[1] = find_executable(argv[0]); + if (newargz[1] == NULL) + lt_fatal("Couldn't find %s", argv[0]); + DEBUG("(main) found exe at : %s\n",newargz[1]); + /* we know the script has the same name, without the .exe */ + /* so make sure newargz[1] doesn't end in .exe */ + strendzap(newargz[1],".exe"); + for (i = 1; i < argc; i++) + newargz[i+1] = xstrdup(argv[i]); + newargz[argc+1] = NULL; + + for (i=0; i> $cwrappersource <> $cwrappersource <> $cwrappersource <<"EOF" + return 127; +} + +void * +xmalloc (size_t num) +{ + void * p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL +; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char)name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable(const char * path) +{ + struct stat st; + + DEBUG("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!"); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) && + ( + /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */ +#if defined (S_IXOTH) + ((st.st_mode & S_IXOTH) == S_IXOTH) || +#endif +#if defined (S_IXGRP) + ((st.st_mode & S_IXGRP) == S_IXGRP) || +#endif + ((st.st_mode & S_IXUSR) == S_IXUSR)) + ) + return 1; + else + return 0; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise */ +char * +find_executable (const char* wrapper) +{ + int has_slash = 0; + const char* p; + const char* p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char* concat_name; + + DEBUG("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char)wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char* path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char* q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR(*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen(tmp); + concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = XMALLOC(char, p_len + 1 + strlen(wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen(tmp); + concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + return NULL; +} + +char * +strendzap(char *str, const char *pat) +{ + size_t len, patlen; + + assert(str != NULL); + assert(pat != NULL); + + len = strlen(str); + patlen = strlen(pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp(str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char * mode, + const char * message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} +EOF + # we should really use a build-platform specific compiler + # here, but OTOH, the wrappers (shell script and this C one) + # are only useful if you want to execute the "real" binary. + # Since the "real" binary is built for $host, then this + # wrapper might as well be built for $host, too. + $run $LTCC $LTCFLAGS -s -o $cwrapper $cwrappersource + ;; + esac + $rm $output + trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 + + $echo > $output "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible (taken from Autoconf:_AS_BOURNE_COMPATIBLE). +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variable: + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + echo=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$echo works! + : + else + # Restart under the correct shell, and then maybe \$echo will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $echo >> $output "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $mkdir \"\$progdir\" + else + $rm \"\$progdir/\$file\" + fi" + + $echo >> $output "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $echo \"\$relink_command_output\" >&2 + $rm \"\$progdir/\$file\" + exit $EXIT_FAILURE + fi + fi + + $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $rm \"\$progdir/\$program\"; + $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $rm \"\$progdir/\$file\" + fi" + else + $echo >> $output "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $echo >> $output "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $echo >> $output "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2*) + $echo >> $output "\ + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $echo >> $output "\ + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $echo >> $output "\ + \$echo \"\$0: cannot exec \$program \$*\" + exit $EXIT_FAILURE + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + $echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit $EXIT_FAILURE + fi +fi\ +" + chmod +x $output + fi + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $addlibs + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + $echo "X$obj" | $Xsed -e 's%^.*/%%' + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "copying selected object files to avoid basename conflicts..." + + if test -z "$gentop"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$gentop"; then + exit $exit_status + fi + fi + + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + objbase=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + counter=`expr $counter + 1` + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + $show "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + $run ln "$obj" "$gentop/$newobj" || + $run cp "$obj" "$gentop/$newobj" + oldobjs="$oldobjs $gentop/$newobj" + ;; + *) oldobjs="$oldobjs $obj" ;; + esac + done + fi + + eval cmds=\"$old_archive_cmds\" + + if len=`expr "X$cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + $echo "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + for obj in $save_oldobjs + do + oldobjs="$objlist $obj" + objlist="$objlist $obj" + eval test_cmds=\"$old_archive_cmds\" + if len=`expr "X$test_cmds" : ".*" 2>/dev/null` && + test "$len" -le "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + eval cmd=\"$cmd\" + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$generated"; then + $show "${rm}r$generated" + $run ${rm}r$generated + fi + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + $show "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$echo "X$relink_command" | $SP2NL | $Xsed -e "$sed_quote_subst" | $NL2SP` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + + # Only create the output if not a dry run. + if test -z "$run"; then + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + for lib in $dlfiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdlfiles="$newdlfiles $libdir/$name" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdlprefiles="$newdlprefiles $libdir/$name" + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $rm $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $echo > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $echo >> $output "\ +relink_command=\"$relink_command\"" + fi + done + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" + $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? + ;; + esac + exit $EXIT_SUCCESS + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $echo "X$nonopt" | grep shtool > /dev/null; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + case " $install_prog " in + *[\\\ /]cp\ *) ;; + *) prev=$arg ;; + esac + ;; + -g | -m | -o) prev=$arg ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test "$#" -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + library_names= + old_library= + relink_command= + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + if test "$inst_prefix_dir" = "$destdir"; then + $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 + exit $EXIT_FAILURE + fi + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$echo "$relink_command" | $SP2NL | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%" | $NL2SP` + else + relink_command=`$echo "$relink_command" | $SP2NL | $SED "s%@inst_prefix_dir@%%" | $NL2SP` + fi + + $echo "$modename: warning: relinking \`$file'" 1>&2 + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + exit $EXIT_FAILURE + fi + fi + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$srcname $destdir/$realname" + $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? + if test -n "$stripme" && test -n "$striplib"; then + $show "$striplib $destdir/$realname" + $run eval "$striplib $destdir/$realname" || exit $? + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + if test "$linkname" != "$realname"; then + $show "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" + $run eval "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" + fi + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + cmds=$postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + file=`$echo $file|${SED} 's,.exe$,,'` + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin*|*mingw*) + wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` + ;; + *) + wrapper=$file + ;; + esac + if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then + notinst_deplibs= + relink_command= + + # Note that it is not necessary on cygwin/mingw to append a dot to + # foo even if both foo and FILE.exe exist: automatic-append-.exe + # behavior happens only for exec(3), not for open(2)! Also, sourcing + # `FILE.' does not work on cygwin managed mounts. + # + # If there is no directory component, then add one. + case $wrapper in + */* | *\\*) . ${wrapper} ;; + *) . ./${wrapper} ;; + esac + + # Check the variables that should have been set. + if test -z "$notinst_deplibs"; then + $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 + exit $EXIT_FAILURE + fi + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + relink_command= + # Note that it is not necessary on cygwin/mingw to append a dot to + # foo even if both foo and FILE.exe exist: automatic-append-.exe + # behavior happens only for exec(3), not for open(2)! Also, sourcing + # `FILE.' does not work on cygwin managed mounts. + # + # If there is no directory component, then add one. + case $wrapper in + */* | *\\*) . ${wrapper} ;; + *) . ./${wrapper} ;; + esac + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + if test "$finalize" = yes && test -z "$run"; then + tmpdir=`func_mktempdir` + file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$echo "X$relink_command" | $SP2NL | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g' | $NL2SP` + + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + ${rm}r "$tmpdir" + continue + fi + file="$outputname" + else + $echo "$modename: warning: cannot relink \`$file'" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` + ;; + esac + ;; + esac + $show "$install_prog$stripme $file $destfile" + $run eval "$install_prog\$stripme \$file \$destfile" || exit $? + test -n "$outputname" && ${rm}r "$tmpdir" + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + if test -n "$stripme" && test -n "$old_striplib"; then + $show "$old_striplib $oldlib" + $run eval "$old_striplib $oldlib" || exit $? + fi + + # Do each command in the postinstall commands. + cmds=$old_postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + cmds=$finish_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || admincmds="$admincmds + $cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + test "$show" = : && exit $EXIT_SUCCESS + + $echo "X----------------------------------------------------------------------" | $Xsed + $echo "Libraries have been installed in:" + for libdir in $libdirs; do + $echo " $libdir" + done + $echo + $echo "If you ever happen to want to link against installed libraries" + $echo "in a given directory, LIBDIR, you must either use libtool, and" + $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + $echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + $echo " during execution" + fi + if test -n "$runpath_var"; then + $echo " - add LIBDIR to the \`$runpath_var' environment variable" + $echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $echo " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $echo " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $echo + $echo "See any operating system documentation about shared libraries for" + $echo "more information, such as the ld(1) and ld.so(8) manual pages." + $echo "X----------------------------------------------------------------------" | $Xsed + exit $EXIT_SUCCESS + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit $EXIT_FAILURE + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test ! -f "$file"; then + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit $EXIT_FAILURE + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + fi" + done + + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + fi + $echo "$cmd$args" + exit $EXIT_SUCCESS + fi + ;; + + # libtool clean and uninstall mode + clean | uninstall) + modename="$modename: $mode" + rm="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) rm="$rm $arg"; rmforce=yes ;; + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$file"; then + dir=. + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if (test -L "$file") >/dev/null 2>&1 \ + || (test -h "$file") >/dev/null 2>&1 \ + || test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + + case "$mode" in + clean) + case " $library_names " in + # " " in the beginning catches empty $dlname + *" $dlname "*) ;; + *) rmfiles="$rmfiles $objdir/$dlname" ;; + esac + test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + cmds=$postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + cmds=$old_postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + + # Read the .lo file + . $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" \ + && test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" \ + && test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + file=`$echo $file|${SED} 's,.exe$,,'` + noexename=`$echo $name|${SED} 's,.exe$,,'` + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + relink_command= + . $dir/$noexename + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + $show "$rm $rmfiles" + $run $rm $rmfiles || exit_status=1 + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + $show "rmdir $dir" + $run rmdir $dir >/dev/null 2>&1 + fi + done + + exit $exit_status + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + if test -z "$exec_cmd"; then + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit $EXIT_FAILURE + fi +fi # test -z "$show_help" + +if test -n "$exec_cmd"; then + eval exec $exec_cmd + exit $EXIT_FAILURE +fi + +# We need to display help for each of the modes. +case $mode in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + + --config show all configuration variables + --debug enable verbose shell tracing +-n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + --version print version information + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE. + +Report bugs to ." + exit $EXIT_SUCCESS + ;; + +clean) + $echo \ +"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -static always build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + +uninstall) + $echo \ +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; +esac + +$echo +$echo "Try \`$modename --help' for more information about other modes." + +exit $? + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +disable_libs=shared +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +disable_libs=static +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/framework/src/audit/m4/ax_prog_cc_for_build.m4 b/framework/src/audit/m4/ax_prog_cc_for_build.m4 new file mode 100644 index 00000000..77fd346a --- /dev/null +++ b/framework/src/audit/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_EXEEXT])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_cpp], ac_build_cpp)dnl +pushdef([ac_compile], ac_build_compile)dnl +pushdef([ac_link], ac_build_link)dnl + +save_cross_compiling=$cross_compiling +save_ac_tool_prefix=$ac_tool_prefix +cross_compiling=no +ac_tool_prefix= + +AC_PROG_CC +AC_PROG_CPP +AC_EXEEXT + +ac_tool_prefix=$save_ac_tool_prefix +cross_compiling=$save_cross_compiling + +dnl Restore the old definitions +dnl +popdef([ac_link])dnl +popdef([ac_compile])dnl +popdef([ac_cpp])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([LDFLAGS])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl Finally, set Makefile variables +dnl +BUILD_EXEEXT=$ac_build_exeext +BUILD_OBJEXT=$ac_build_objext +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) diff --git a/framework/src/audit/m4/cap-ng.m4 b/framework/src/audit/m4/cap-ng.m4 new file mode 100644 index 00000000..0024edc6 --- /dev/null +++ b/framework/src/audit/m4/cap-ng.m4 @@ -0,0 +1,40 @@ +# libcap-ng.m4 - Checks for the libcap-ng support +# Copyright (c) 2009 Steve Grubb sgrubb@redhat.com +# +AC_DEFUN([LIBCAP_NG_PATH], +[ + AC_ARG_WITH(libcap-ng, + [ --with-libcap-ng=[auto/yes/no] Add Libcap-ng support [default=auto]],, + with_libcap_ng=auto) + + # Check for Libcap-ng API + # + # libcap-ng detection + + if test x$with_libcap_ng = xno ; then + have_libcap_ng=no; + else + # Start by checking for header file + AC_CHECK_HEADER(cap-ng.h, capng_headers=yes, capng_headers=no) + + # See if we have libcap-ng library + AC_CHECK_LIB(cap-ng, capng_clear, + CAPNG_LDADD=-lcap-ng,) + + # Check results are usable + if test x$with_libcap_ng = xyes -a x$CAPNG_LDADD = x ; then + AC_MSG_ERROR(libcap-ng support was requested and the library was not found) + fi + if test x$CAPNG_LDADD != x -a $capng_headers = no ; then + AC_MSG_ERROR(libcap-ng libraries found but headers are missing) + fi + fi + AC_SUBST(CAPNG_LDADD) + AC_MSG_CHECKING(whether to use libcap-ng) + if test x$CAPNG_LDADD != x ; then + AC_DEFINE(HAVE_LIBCAP_NG,1,[libcap-ng support]) + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +]) diff --git a/framework/src/audit/missing b/framework/src/audit/missing new file mode 100755 index 00000000..cdea5149 --- /dev/null +++ b/framework/src/audit/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2012-06-26.16; # UTC + +# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'automa4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/framework/src/audit/py-compile b/framework/src/audit/py-compile new file mode 100755 index 00000000..69169037 --- /dev/null +++ b/framework/src/audit/py-compile @@ -0,0 +1,160 @@ +#!/bin/sh +# py-compile - Compile a Python program + +scriptversion=2011-06-08.12; # UTC + +# Copyright (C) 2000-2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +if [ -z "$PYTHON" ]; then + PYTHON=python +fi + +me=py-compile + +usage_error () +{ + echo "$me: $*" >&2 + echo "Try '$me --help' for more information." >&2 + exit 1 +} + +basedir= +destdir= +while test $# -ne 0; do + case "$1" in + --basedir) + if test $# -lt 2; then + usage_error "option '--basedir' requires an argument" + else + basedir=$2 + fi + shift + ;; + --destdir) + if test $# -lt 2; then + usage_error "option '--destdir' requires an argument" + else + destdir=$2 + fi + shift + ;; + -h|--help) + cat <<\EOF +Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..." + +Byte compile some python scripts FILES. Use --destdir to specify any +leading directory path to the FILES that you don't want to include in the +byte compiled file. Specify --basedir for any additional path information you +do want to be shown in the byte compiled file. + +Example: + py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py + +Report bugs to . +EOF + exit $? + ;; + -v|--version) + echo "$me $scriptversion" + exit $? + ;; + --) + shift + break + ;; + -*) + usage_error "unrecognized option '$1'" + ;; + *) + break + ;; + esac + shift +done + +files=$* +if test -z "$files"; then + usage_error "no files given" +fi + +# if basedir was given, then it should be prepended to filenames before +# byte compilation. +if [ -z "$basedir" ]; then + pathtrans="path = file" +else + pathtrans="path = os.path.join('$basedir', file)" +fi + +# if destdir was given, then it needs to be prepended to the filename to +# byte compile but not go into the compiled file. +if [ -z "$destdir" ]; then + filetrans="filepath = path" +else + filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)" +fi + +$PYTHON -c " +import sys, os, py_compile + +files = '''$files''' + +sys.stdout.write('Byte-compiling python modules...\n') +for file in files.split(): + $pathtrans + $filetrans + if not os.path.exists(filepath) or not (len(filepath) >= 3 + and filepath[-3:] == '.py'): + continue + sys.stdout.write(file) + sys.stdout.flush() + py_compile.compile(filepath, filepath + 'c', path) +sys.stdout.write('\n')" || exit $? + +# this will fail for python < 1.5, but that doesn't matter ... +$PYTHON -O -c " +import sys, os, py_compile + +files = '''$files''' +sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n') +for file in files.split(): + $pathtrans + $filetrans + if not os.path.exists(filepath) or not (len(filepath) >= 3 + and filepath[-3:] == '.py'): + continue + sys.stdout.write(file) + sys.stdout.flush() + py_compile.compile(filepath, filepath + 'o', path) +sys.stdout.write('\n')" 2>/dev/null || : + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/framework/src/audit/src/Makefile.am b/framework/src/audit/src/Makefile.am new file mode 100644 index 00000000..8d1af865 --- /dev/null +++ b/framework/src/audit/src/Makefile.am @@ -0,0 +1,57 @@ +# Makefile.am-- +# Copyright 2004-2006, 2008,2011-15 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +SUBDIRS = test +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib -I${top_srcdir}/src/libev -I${top_srcdir}/auparse +sbin_PROGRAMS = auditd auditctl aureport ausearch autrace +AM_CFLAGS = -D_GNU_SOURCE +noinst_HEADERS = auditd-config.h auditd-event.h auditd-listen.h ausearch-llist.h ausearch-options.h auditctl-llist.h aureport-options.h ausearch-parse.h aureport-scan.h ausearch-lookup.h ausearch-int.h auditd-dispatch.h ausearch-string.h ausearch-nvpair.h ausearch-common.h ausearch-avc.h ausearch-time.h ausearch-lol.h auditctl-listing.h ausearch-checkpt.h + +auditd_SOURCES = auditd.c auditd-event.c auditd-config.c auditd-reconfig.c auditd-sendmail.c auditd-dispatch.c +if ENABLE_LISTENER +auditd_SOURCES += auditd-listen.c +endif +auditd_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pthread +auditd_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now +auditd_DEPENDENCIES = mt/libauditmt.a libev/libev.a +auditd_LDADD = @LIBWRAP_LIBS@ -Llibev -lev -Lmt -lauditmt -lpthread -lrt -lm $(gss_libs) + +auditctl_SOURCES = auditctl.c auditctl-llist.c delete_all.c auditctl-listing.c +auditctl_CFLAGS = -fPIE -DPIE -g -D_GNU_SOURCE +auditctl_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now +auditctl_LDADD = -L${top_builddir}/lib -laudit -L${top_builddir}/auparse -lauparse + +aureport_SOURCES = aureport.c auditd-config.c ausearch-llist.c aureport-options.c ausearch-string.c ausearch-parse.c aureport-scan.c aureport-output.c ausearch-lookup.c ausearch-int.c ausearch-time.c ausearch-nvpair.c ausearch-avc.c ausearch-lol.c +aureport_LDADD = -L${top_builddir}/lib -laudit + +ausearch_SOURCES = ausearch.c auditd-config.c ausearch-llist.c ausearch-options.c ausearch-report.c ausearch-match.c ausearch-string.c ausearch-parse.c ausearch-int.c ausearch-time.c ausearch-nvpair.c ausearch-lookup.c ausearch-avc.c ausearch-lol.c ausearch-checkpt.c +ausearch_LDADD = -L${top_builddir}/lib -laudit -L${top_builddir}/auparse -lauparse + +autrace_SOURCES = autrace.c delete_all.c auditctl-llist.c +autrace_LDADD = -L${top_builddir}/lib -laudit + +mt/libauditmt.a: + make -C mt +libev/libev.a: + make -C libev diff --git a/framework/src/audit/src/auditctl-listing.c b/framework/src/audit/src/auditctl-listing.c new file mode 100644 index 00000000..88dac6c8 --- /dev/null +++ b/framework/src/audit/src/auditctl-listing.c @@ -0,0 +1,577 @@ +/* auditctl-listing.c -- + * Copyright 2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include "auditctl-listing.h" +#include "private.h" +#include "auditctl-llist.h" +#include "auparse-idata.h" + +/* Global vars */ +static llist l; +static int printed; +extern int list_requested, interpret; +extern char key[AUDIT_MAX_KEY_LEN+1]; +extern const char key_sep[2]; + +/* + * Returns 1 if rule should be printed & 0 if not + */ +int key_match(const struct audit_rule_data *r) +{ + int i; + size_t boffset = 0; + + if (key[0] == 0) + return 1; + + // At this point, we have a key + for (i = 0; i < r->field_count; i++) { + int field = r->fields[i] & ~AUDIT_OPERATORS; + if (field == AUDIT_FILTERKEY) { + char *keyptr; + if (asprintf(&keyptr, "%.*s", r->values[i], + &r->buf[boffset]) < 0) + keyptr = NULL; + else if (strstr(keyptr, key)) { + free(keyptr); + return 1; + } + free(keyptr); + } + if (((field >= AUDIT_SUBJ_USER && field <= AUDIT_OBJ_LEV_HIGH) + && field != AUDIT_PPID) || field == AUDIT_WATCH || + field == AUDIT_DIR || field == AUDIT_FILTERKEY) { + boffset += r->values[i]; + } + } + return 0; +} + +/* + * This function detects if we have a watch. A watch is detected when we + * have syscall == all and a perm field. + */ +static int is_watch(const struct audit_rule_data *r) +{ + int i, perm = 0, all = 1; + + for (i = 0; i < r->field_count; i++) { + int field = r->fields[i] & ~AUDIT_OPERATORS; + if (field == AUDIT_PERM) + perm = 1; + // Watches can have only 4 field types + if (field != AUDIT_PERM && field != AUDIT_FILTERKEY && + field != AUDIT_DIR && field != AUDIT_WATCH) + return 0; + } + + if (((r->flags & AUDIT_FILTER_MASK) != AUDIT_FILTER_USER) && + ((r->flags & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK) && + ((r->flags & AUDIT_FILTER_MASK) != AUDIT_FILTER_EXCLUDE)) { + for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++) { + if (r->mask[i] != (uint32_t)~0) { + all = 0; + break; + } + } + } + if (perm && all) + return 1; + return 0; +} + +static int print_arch(unsigned int value, int op) +{ + int machine; + _audit_elf = value; + machine = audit_elf_to_machine(_audit_elf); + if (machine < 0) + printf(" -F arch%s0x%X", audit_operator_to_symbol(op), + (unsigned)value); + else { + if (interpret == 0) { + if (__AUDIT_ARCH_64BIT & _audit_elf) + printf(" -F arch%sb64", + audit_operator_to_symbol(op)); + else + printf(" -F arch%sb32", + audit_operator_to_symbol(op)); + } else { + const char *ptr = audit_machine_to_name(machine); + printf(" -F arch%s%s", audit_operator_to_symbol(op), + ptr); + } + } + return machine; +} + +static int print_syscall(const struct audit_rule_data *r, unsigned int *sc) +{ + int count = 0; + int all = 1; + unsigned int i; + int machine = audit_detect_machine(); + + /* Rules on the following filters do not take a syscall */ + if (((r->flags & AUDIT_FILTER_MASK) == AUDIT_FILTER_USER) || + ((r->flags & AUDIT_FILTER_MASK) == AUDIT_FILTER_TASK) || + ((r->flags &AUDIT_FILTER_MASK) == AUDIT_FILTER_EXCLUDE)) + return 0; + + /* See if its all or specific syscalls */ + for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++) { + if (r->mask[i] != (uint32_t)~0) { + all = 0; + break; + } + } + + if (all) { + printf(" -S all"); + count = i; + } else for (i = 0; i < AUDIT_BITMASK_SIZE * 32; i++) { + int word = AUDIT_WORD(i); + int bit = AUDIT_BIT(i); + if (r->mask[word] & bit) { + const char *ptr; + if (_audit_elf) + machine = audit_elf_to_machine(_audit_elf); + if (machine < 0) + ptr = NULL; + else + ptr = audit_syscall_to_name(i, machine); + if (!count) + printf(" -S "); + if (ptr) + printf("%s%s", !count ? "" : ",", ptr); + else + printf("%s%d", !count ? "" : ",", i); + count++; + *sc = i; + } + } + return count; +} + +static void print_field_cmp(int value, int op) +{ + switch (value) + { + case AUDIT_COMPARE_UID_TO_OBJ_UID: + printf(" -C uid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_OBJ_GID: + printf(" -C gid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EUID_TO_OBJ_UID: + printf(" -C euid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EGID_TO_OBJ_GID: + printf(" -C egid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_OBJ_UID: + printf(" -C auid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SUID_TO_OBJ_UID: + printf(" -C suid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SGID_TO_OBJ_GID: + printf(" -C sgid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_FSUID_TO_OBJ_UID: + printf(" -C fsuid%sobj_uid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_FSGID_TO_OBJ_GID: + printf(" -C fsgid%sobj_gid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_AUID: + printf(" -C uid%sauid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_EUID: + printf(" -C uid%seuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_FSUID: + printf(" -C uid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_UID_TO_SUID: + printf(" -C uid%ssuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_FSUID: + printf(" -C auid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_SUID: + printf(" -C auid%ssuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_AUID_TO_EUID: + printf(" -C auid%seuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EUID_TO_SUID: + printf(" -C euid%ssuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EUID_TO_FSUID: + printf(" -C euid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SUID_TO_FSUID: + printf(" -C suid%sfsuid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_EGID: + printf(" -C gid%segid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_FSGID: + printf(" -C gid%sfsgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_GID_TO_SGID: + printf(" -C gid%ssgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EGID_TO_FSGID: + printf(" -C egid%sfsgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_EGID_TO_SGID: + printf(" -C egid%ssgid", + audit_operator_to_symbol(op)); + break; + case AUDIT_COMPARE_SGID_TO_FSGID: + printf(" -C sgid%sfsgid", + audit_operator_to_symbol(op)); + break; + } +} + +/* + * This function prints 1 rule from the kernel reply + */ +static void print_rule(const struct audit_rule_data *r) +{ + unsigned int i, count = 0, sc = 0; + size_t boffset = 0; + int mach = -1, watch = is_watch(r); + unsigned long long a0 = 0, a1 = 0; + + if (!watch) { /* This is syscall auditing */ + printf("-a %s,%s", + audit_action_to_name((int)r->action), + audit_flag_to_name(r->flags)); + + // Now find the arch and print it + for (i = 0; i < r->field_count; i++) { + int field = r->fields[i] & ~AUDIT_OPERATORS; + if (field == AUDIT_ARCH) { + int op = r->fieldflags[i] & AUDIT_OPERATORS; + mach = print_arch(r->values[i], op); + } + } + // And last do the syscalls + count = print_syscall(r, &sc); + } + + // Now iterate over the fields + for (i = 0; i < r->field_count; i++) { + const char *name; + int op = r->fieldflags[i] & AUDIT_OPERATORS; + int field = r->fields[i] & ~AUDIT_OPERATORS; + + if (field == AUDIT_ARCH) + continue; // already printed + + name = audit_field_to_name(field); + if (name) { + // Special cases to print the different field types + // in a meaningful way. + if (field == AUDIT_MSGTYPE) { + if (!audit_msg_type_to_name(r->values[i])) + printf(" -F %s%s%d", name, + audit_operator_to_symbol(op), + r->values[i]); + else + printf(" -F %s%s%s", name, + audit_operator_to_symbol(op), + audit_msg_type_to_name( + r->values[i])); + } else if ((field >= AUDIT_SUBJ_USER && + field <= AUDIT_OBJ_LEV_HIGH) + && field != AUDIT_PPID) { + printf(" -F %s%s%.*s", name, + audit_operator_to_symbol(op), + r->values[i], &r->buf[boffset]); + boffset += r->values[i]; + } else if (field == AUDIT_WATCH) { + if (watch) + printf("-w %.*s", r->values[i], + &r->buf[boffset]); + else + printf(" -F path=%.*s", r->values[i], + &r->buf[boffset]); + boffset += r->values[i]; + } else if (field == AUDIT_DIR) { + if (watch) + printf("-w %.*s/", r->values[i], + &r->buf[boffset]); + else + printf(" -F dir=%.*s", r->values[i], + &r->buf[boffset]); + + boffset += r->values[i]; + } else if (field == AUDIT_FILTERKEY) { + char *rkey, *ptr, *saved; + if (asprintf(&rkey, "%.*s", r->values[i], + &r->buf[boffset]) < 0) + rkey = NULL; + boffset += r->values[i]; + ptr = strtok_r(rkey, key_sep, &saved); + while (ptr) { + if (watch) + printf(" -k %s", ptr); + else + printf(" -F key=%s", ptr); + ptr = strtok_r(NULL, key_sep, &saved); + } + free(rkey); + } else if (field == AUDIT_PERM) { + char perms[5]; + int val=r->values[i]; + perms[0] = 0; + if (val & AUDIT_PERM_READ) + strcat(perms, "r"); + if (val & AUDIT_PERM_WRITE) + strcat(perms, "w"); + if (val & AUDIT_PERM_EXEC) + strcat(perms, "x"); + if (val & AUDIT_PERM_ATTR) + strcat(perms, "a"); + if (watch) + printf(" -p %s", perms); + else + printf(" -F perm=%s", perms); + } else if (field == AUDIT_INODE) { + // This is unsigned + printf(" -F %s%s%u", name, + audit_operator_to_symbol(op), + r->values[i]); + } else if (field == AUDIT_FIELD_COMPARE) { + print_field_cmp(r->values[i], op); + } else if (field >= AUDIT_ARG0 && field <= AUDIT_ARG3){ + if (field == AUDIT_ARG0) + a0 = r->values[i]; + else if (field == AUDIT_ARG1) + a1 = r->values[i]; + + // Show these as hex + if (count > 1 || interpret == 0) + printf(" -F %s%s0x%X", name, + audit_operator_to_symbol(op), + r->values[i]); + else { // Use ignore to mean interpret + const char *out; + idata id; + char val[32]; + int type; + + id.syscall = sc; + id.machine = mach; + id.a0 = a0; + id.a1 = a1; + id.name = name; + snprintf(val, 32, "%x", r->values[i]); + id.val = val; + type = auparse_interp_adjust_type( + AUDIT_SYSCALL, name, val); + out = auparse_do_interpretation(type, + &id); + printf(" -F %s%s%s", name, + audit_operator_to_symbol(op), + out); + free((void *)out); + } + } else if (field == AUDIT_EXIT) { + int e = abs((int)r->values[i]); + const char *err = audit_errno_to_name(e); + + if (((int)r->values[i] < 0) && err) + printf(" -F %s%s-%s", name, + audit_operator_to_symbol(op), + err); + else + printf(" -F %s%s%d", name, + audit_operator_to_symbol(op), + (int)r->values[i]); + } else { + // The default is signed decimal + printf(" -F %s%s%d", name, + audit_operator_to_symbol(op), + r->values[i]); + } + } else { + // The field name is unknown + printf(" f%d%s%d", r->fields[i], + audit_operator_to_symbol(op), + r->values[i]); + } + } + printf("\n"); +} + +void audit_print_init(void) +{ + printed = 0; + list_create(&l); +} + +const char *get_enable(unsigned e) +{ + switch (e) + { + case 0: + return "disable"; + case 1: + return "enabled"; + case 2: + return "enabl;ed+immutable"; + default: + return "unknown"; + } +} + +const char *get_failure(unsigned f) +{ + switch (f) + { + case 0: + return "silent"; + case 1: + return "printk"; + case 2: + return "panic"; + default: + return "unknown"; + } +} + +/* + * This function interprets the reply and prints it to stdout. It returns + * 0 if no more should be read and 1 to indicate that more messages of this + * type may need to be read. + */ +int audit_print_reply(struct audit_reply *rep, int fd) +{ + _audit_elf = 0; + + switch (rep->type) { + case NLMSG_NOOP: + return 1; + case NLMSG_DONE: + // Close the socket so kernel can do other things + audit_close(fd); + if (printed == 0) + printf("No rules\n"); + else { + lnode *n; + list_first(&l); + n = l.cur; + while (n) { + print_rule(n->r); + n = list_next(&l); + } + list_clear(&l); + } + break; + case NLMSG_ERROR: + printf("NLMSG_ERROR %d (%s)\n", + -rep->error->error, + strerror(-rep->error->error)); + printed = 1; + break; + case AUDIT_GET: + if (interpret) + printf("enabled %s\nfailure %s\n", + get_enable(rep->status->enabled), + get_failure(rep->status->failure)); + else + printf("enabled %u\nfailure %u\n", + rep->status->enabled, rep->status->failure); + printf("pid %u\nrate_limit %u\nbacklog_limit %u\n" + "lost %u\nbacklog %u\n", + rep->status->pid, rep->status->rate_limit, + rep->status->backlog_limit, rep->status->lost, + rep->status->backlog); +#if HAVE_DECL_AUDIT_VERSION_BACKLOG_WAIT_TIME + printf("backlog_wait_time %u\n", + rep->status->backlog_wait_time); +#endif + printed = 1; + break; +#if HAVE_DECL_AUDIT_FEATURE_VERSION + case AUDIT_GET_FEATURE: + { + uint32_t mask = AUDIT_FEATURE_TO_MASK(AUDIT_FEATURE_LOGINUID_IMMUTABLE); + if (rep->features->mask & mask) + printf("loginuid_immutable %u %s\n", + !!(rep->features->features & mask), + rep->features->lock & mask ? "locked" : + "unlocked"); + } + printed = 1; + break; +#endif + case AUDIT_LIST_RULES: + list_requested = 0; + if (key_match(rep->ruledata)) + list_append(&l, rep->ruledata, + sizeof(struct audit_rule_data) + + rep->ruledata->buflen); + printed = 1; + return 1; + default: + printf("Unknown: type=%d, len=%d\n", rep->type, + rep->nlh->nlmsg_len); + printed = 1; + break; + } + return 0; +} + diff --git a/framework/src/audit/src/auditctl-listing.h b/framework/src/audit/src/auditctl-listing.h new file mode 100644 index 00000000..8a412ef3 --- /dev/null +++ b/framework/src/audit/src/auditctl-listing.h @@ -0,0 +1,34 @@ +/* +* auditctl-listing.h - Header file for ausearch-llist.c +* Copyright (c) 2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef CTLLISTING_HEADER +#define CTLLISTING_HEADER + +#include "config.h" +#include "libaudit.h" + +void audit_print_init(void); +int audit_print_reply(struct audit_reply *rep, int fd); +int key_match(const struct audit_rule_data *r); + +#endif diff --git a/framework/src/audit/src/auditctl-llist.c b/framework/src/audit/src/auditctl-llist.c new file mode 100644 index 00000000..175310d7 --- /dev/null +++ b/framework/src/audit/src/auditctl-llist.c @@ -0,0 +1,105 @@ +/* +* ausearch-llist.c - Minimal linked list library +* Copyright (c) 2005 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include +#include +#include "auditctl-llist.h" + +void list_create(llist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +void list_first(llist *l) +{ + l->cur = l->head; +} + +void list_last(llist *l) +{ + register lnode* window; + + if (l->head == NULL) + return; + + window = l->head; + while (window->next) + window = window->next; + l->cur = window; +} + +lnode *list_next(llist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +void list_append(llist *l, struct audit_rule_data *r, size_t sz) +{ + lnode* newnode; + + newnode = malloc(sizeof(lnode)); + + if (r) { + void *rr = malloc(sz); + if (rr) + memcpy(rr, r, sz); + newnode->r = rr; + } else + newnode->r = NULL; + + newnode->size = sz; + newnode->next = 0; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +void list_clear(llist* l) +{ + lnode* nextnode; + register lnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->r); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + diff --git a/framework/src/audit/src/auditctl-llist.h b/framework/src/audit/src/auditctl-llist.h new file mode 100644 index 00000000..371d0d7e --- /dev/null +++ b/framework/src/audit/src/auditctl-llist.h @@ -0,0 +1,56 @@ +/* +* auditctl-llist.h - Header file for ausearch-llist.c +* Copyright (c) 2005 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef CTLLIST_HEADER +#define CTLLIST_HEADER + +#include "config.h" +#include +#include "libaudit.h" + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _lnode{ + struct audit_rule_data *r; // The rule from the kernel + size_t size; // Size of the rule struct + struct _lnode *next; // Next node pointer +} lnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + lnode *head; // List head + lnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} llist; + +void list_create(llist *l); +void list_first(llist *l); +void list_last(llist *l); +lnode *list_next(llist *l); +static inline lnode *list_get_cur(llist *l) { return l->cur; } +void list_append(llist *l, struct audit_rule_data *r, size_t sz); +void list_clear(llist* l); + +#endif + diff --git a/framework/src/audit/src/auditctl.c b/framework/src/audit/src/auditctl.c new file mode 100644 index 00000000..334f8021 --- /dev/null +++ b/framework/src/audit/src/auditctl.c @@ -0,0 +1,1472 @@ +/* auditctl.c -- + * Copyright 2004-2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Rickard E. (Rik) Faith + */ + +#include "config.h" +#include +#include +#include +#include /* strdup needs xopen define */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For basename */ +#include /* PATH_MAX */ +#include "libaudit.h" +#include "auditctl-listing.h" +#include "private.h" + +/* This define controls the size of the line that we will request when + * reading in rules from a file. + */ +#define LINE_SIZE 6144 + + +/* Global functions */ +static int handle_request(int status); +static void get_reply(void); +extern int delete_all_rules(int fd); + +/* Global vars */ +int list_requested = 0, interpret = 0; +char key[AUDIT_MAX_KEY_LEN+1]; +const char key_sep[2] = { AUDIT_KEY_SEPARATOR, 0 }; +static int keylen; +static int fd = -1; +static int add = AUDIT_FILTER_UNSET, del = AUDIT_FILTER_UNSET, action = -1; +static int ignore = 0, continue_error = 0; +static int exclude = 0; +static int multiple = 0; +static struct audit_rule_data *rule_new = NULL; + +/* + * This function will reset everything used for each loop when loading + * a ruleset from a file. + */ +static int reset_vars(void) +{ + list_requested = 0; + _audit_syscalladded = 0; + _audit_permadded = 0; + _audit_archadded = 0; + _audit_elf = 0; + add = AUDIT_FILTER_UNSET; + del = AUDIT_FILTER_UNSET; + action = -1; + exclude = 0; + multiple = 0; + + free(rule_new); + rule_new = malloc(sizeof(struct audit_rule_data)); + memset(rule_new, 0, sizeof(struct audit_rule_data)); + if (fd < 0) { + if ((fd = audit_open()) < 0) { + audit_msg(LOG_ERR, "Cannot open netlink audit socket"); + return 1; + } + } + return 0; +} + +static void usage(void) +{ + printf( + "usage: auditctl [options]\n" + " -a Append rule to end of ist with ction\n" + " -A Add rule at beginning of ist with ction\n" + " -b Set max number of outstanding audit buffers\n" + " allowed Default=64\n" + " -c Continue through errors in rules\n" + " -C f=f Compare collected fields if available:\n" + " Field name, operator(=,!=), field name\n" + " -d Delete rule from ist with ction\n" + " l=task,exit,user,exclude\n" + " a=never,always\n" + " -D Delete all rules and watches\n" + " -e [0..2] Set enabled flag\n" + " -f [0..2] Set failure flag\n" + " 0=silent 1=printk 2=panic\n" + " -F f=v Build rule: field name, operator(=,!=,<,>,<=,\n" + " >=,&,&=) value\n" + " -h Help\n" + " -i Ignore errors when reading rules from file\n" + " -k Set filter key on audit rule\n" + " -l List rules\n" + " -m text Send a user-space message\n" + " -p [r|w|x|a] Set permissions filter on watch\n" + " r=read, w=write, x=execute, a=attribute\n" + " -q make subtree part of mount point's dir watches\n" + " -r Set limit in messages/sec (0=none)\n" + " -R read rules from file\n" + " -s Report status\n" + " -S syscall Build rule: syscall name or number\n" + " -t Trim directory watches\n" + " -v Version\n" + " -w Insert watch at \n" + " -W Remove watch at \n" + " --loginuid-immutable Make loginuids unchangeable once set\n" + " --backlog_wait_time Set the kernel backlog_wait_time\n" + ); +} + +static int lookup_filter(const char *str, int *filter) +{ + if (strcmp(str, "task") == 0) + *filter = AUDIT_FILTER_TASK; + else if (strcmp(str, "entry") == 0) + *filter = AUDIT_FILTER_ENTRY; + else if (strcmp(str, "exit") == 0) + *filter = AUDIT_FILTER_EXIT; + else if (strcmp(str, "user") == 0) + *filter = AUDIT_FILTER_USER; + else if (strcmp(str, "exclude") == 0) { + *filter = AUDIT_FILTER_EXCLUDE; + exclude = 1; + } else + return 2; + return 0; +} + +static int lookup_action(const char *str, int *act) +{ + if (strcmp(str, "never") == 0) + *act = AUDIT_NEVER; + else if (strcmp(str, "possible") == 0) + return 1; + else if (strcmp(str, "always") == 0) + *act = AUDIT_ALWAYS; + else + return 2; + return 0; +} + +/* + * Returns 0 ok, 1 deprecated action, 2 rule error, + * 3 multiple rule insert/delete + */ +static int audit_rule_setup(char *opt, int *filter, int *act, int lineno) +{ + int rc; + char *p; + + if (++multiple != 1) + return 3; + + p = strchr(opt, ','); + if (p == NULL || strchr(p+1, ',')) + return 2; + *p = 0; + + /* Try opt both ways */ + if (lookup_filter(opt, filter) == 2) { + rc = lookup_action(opt, act); + if (rc != 0) { + *p = ','; + return rc; + } + } + + /* Repair the string */ + *p = ','; + opt = p+1; + + /* If flags are empty, p+1 must be the filter */ + if (*filter == AUDIT_FILTER_UNSET) + lookup_filter(opt, filter); + else { + rc = lookup_action(opt, act); + if (rc != 0) + return rc; + } + + /* Make sure we set both */ + if (*filter == AUDIT_FILTER_UNSET || *act == -1) + return 2; + + /* Consolidate rules on exit filter */ + if (*filter == AUDIT_FILTER_ENTRY) { + *filter = AUDIT_FILTER_EXIT; + if (lineno) + audit_msg(LOG_INFO, "Warning - entry rules deprecated, changing to exit rule in line %d", lineno); + else + audit_msg(LOG_INFO, + "Warning - entry rules deprecated, changing to exit rule"); + } + + return 0; +} + +/* + * This function will check the path before accepting it. It returns + * 1 on error and 0 on success. + */ +static int check_path(const char *path) +{ + char *ptr, *base; + size_t nlen; + size_t plen = strlen(path); + if (plen >= PATH_MAX) { + audit_msg(LOG_ERR, "The path passed for the watch is too big"); + return 1; + } + if (path[0] != '/') { + audit_msg(LOG_ERR, "The path must start with '/'"); + return 1; + } + ptr = strdup(path); + base = basename(ptr); + nlen = strlen(base); + free(ptr); + if (nlen > NAME_MAX) { + audit_msg(LOG_ERR, "The base name of the path is too big"); + return 1; + } + + /* These are warnings, not errors */ + if (strstr(path, "..")) + audit_msg(LOG_WARNING, + "Warning - relative path notation is not supported"); + if (strchr(path, '*') || strchr(path, '?')) + audit_msg(LOG_WARNING, + "Warning - wildcard notation is not supported"); + + return 0; +} + +/* + * Setup a watch. The "name" of the watch in userspace will be the to + * the watch. When this potential watch reaches the kernel, it will resolve + * down to (of terminating file or directory). + * Returns a 1 on success & -1 on failure. + */ +static int audit_setup_watch_name(struct audit_rule_data **rulep, char *path) +{ + int type = AUDIT_WATCH; + size_t len; + struct stat buf; + + if (check_path(path)) + return -1; + + // Trim trailing '/' should they exist + len = strlen(path); + if (len > 2 && path[len-1] == '/') { + while (path[len-1] == '/' && len > 1) { + path[len-1] = 0; + len--; + } + } + if (stat(path, &buf) == 0) { + if (S_ISDIR(buf.st_mode)) + type = AUDIT_DIR; + } + /* FIXME: might want to check to see that rule is empty */ + if (audit_add_watch_dir(type, rulep, path)) + return -1; + + return 1; +} + +/* + * Setup a watch permissions. + * Returns a 1 on success & -1 on failure. + */ +static int audit_setup_perms(struct audit_rule_data *rule, const char *opt) +{ + unsigned int i, len, val = 0; + + len = strlen(opt); + if (len > 4) + return -1; + + for (i = 0; i < len; i++) { + switch (tolower(opt[i])) { + case 'r': + val |= AUDIT_PERM_READ; + break; + case 'w': + val |= AUDIT_PERM_WRITE; + break; + case 'x': + val |= AUDIT_PERM_EXEC; + break; + case 'a': + val |= AUDIT_PERM_ATTR; + break; + default: + audit_msg(LOG_ERR, + "Permission %c isn't supported", + opt[i]); + return -1; + } + } + + if (audit_update_watch_perms(rule_new, val) == 0) { + _audit_permadded = 1; + return 1; + } + return -1; +} + +/* 0 success, -1 failure */ +static int lookup_itype(const char *kind) +{ + if (strcmp(kind, "sys") == 0) + return 0; + if (strcmp(kind, "file") == 0) + return 0; + if (strcmp(kind, "exec") == 0) + return 0; + if (strcmp(kind, "mkexe") == 0) + return 0; + return -1; +} + +/* 0 success, -1 failure */ +static int lookup_iseverity(const char *severity) +{ + if (strncmp(severity, "inf", 3) == 0) + return 0; + if (strncmp(severity, "low", 3) == 0) + return 0; + if (strncmp(severity, "med", 3) == 0) + return 0; + if (strncmp(severity, "hi", 2) == 0) + return 0; + return -1; +} + +/* 0 success, -1 failure */ +static int check_ids_key(const char *k) +{ + char *ptr, *kindptr, *ratingptr; + char keyptr[AUDIT_MAX_KEY_LEN+1]; + + if (strlen(k) > AUDIT_MAX_KEY_LEN) + goto fail_exit; + + strncpy(keyptr, k, sizeof(keyptr)); + keyptr[AUDIT_MAX_KEY_LEN] = 0; + ptr = strchr(keyptr, '-'); // There has to be a - because strncmp + kindptr = ptr + 1; + if (*kindptr == 0) + goto fail_exit; + + ptr = strchr(kindptr, '-'); + if (ptr) { + *ptr = 0; + ratingptr = ptr +1; + } else // The rules are misconfigured + goto fail_exit; + if (*ratingptr == 0) + goto fail_exit; + + if (lookup_itype(kindptr)) { + audit_msg(LOG_ERR, "ids key type is bad"); + return -1; + } + if (lookup_iseverity(ratingptr)) { + audit_msg(LOG_ERR, "ids key severity is bad"); + return -1; + } + return 0; + +fail_exit: + audit_msg(LOG_ERR, "ids key is bad"); + return -1; +} + +static int equiv_parse(char *optarg, char **mp, char **sub) +{ + char *ptr = strchr(optarg, ','); + if (ptr == NULL) + return -1; // no comma + *ptr = 0; + ptr++; + if (*ptr == 0) + return -1; // ends with comma + *mp = optarg; + *sub = ptr; + if (strchr(*sub, ',')) + return -1; // too many commas + return 0; +} + +int audit_request_rule_list(int fd) +{ + if (audit_request_rules_list_data(fd) > 0) { + list_requested = 1; + get_reply(); + return 1; + } + return 0; +} + +void check_rule_mismatch(int lineno, const char *option) +{ + struct audit_rule_data tmprule; + unsigned int old_audit_elf = _audit_elf; + int rc = 0; + + switch (_audit_elf) + { + case AUDIT_ARCH_X86_64: + _audit_elf = AUDIT_ARCH_I386; + break; + case AUDIT_ARCH_PPC64: + _audit_elf = AUDIT_ARCH_PPC; + break; + case AUDIT_ARCH_S390X: + _audit_elf = AUDIT_ARCH_S390; + break; + } + memset(&tmprule, 0, sizeof(struct audit_rule_data)); + audit_rule_syscallbyname_data(&tmprule, option); + if (memcmp(tmprule.mask, rule_new->mask, AUDIT_BITMASK_SIZE)) + rc = 1; + _audit_elf = old_audit_elf; + if (rc) { + if (lineno) + audit_msg(LOG_WARNING, "WARNING - 32/64 bit syscall mismatch in line %d, you should specify an arch", lineno); + else + audit_msg(LOG_WARNING, "WARNING - 32/64 bit syscall mismatch, you should specify an arch"); + } +} + +int report_status(int fd) +{ + int retval; + + retval = audit_request_status(fd); + if (retval == -1) { + if (errno == ECONNREFUSED) + fprintf(stderr, "The audit system is disabled\n"); + return -1; + } + get_reply(); + retval = audit_request_features(fd); + if (retval == -1) { + // errno is EINVAL if the kernel does support features API + if (errno == EINVAL) + return -2; + return -1; + } + get_reply(); + return -2; +} + +int parse_syscall(struct audit_rule_data *rule_new, const char *optarg) +{ + int retval = 0; + char *saved; + + if (strchr(optarg, ',')) { + char *ptr, *tmp = strdup(optarg); + if (tmp == NULL) + return -1; + ptr = strtok_r(tmp, ",", &saved); + while (ptr) { + retval = audit_rule_syscallbyname_data(rule_new, ptr); + if (retval != 0) { + if (retval == -1) { + audit_msg(LOG_ERR, + "Syscall name unknown: %s", + ptr); + retval = -3; // error reported + } + break; + } + ptr = strtok_r(NULL, ",", &saved); + } + free(tmp); + return retval; + } + + return audit_rule_syscallbyname_data(rule_new, optarg); +} + +struct option long_opts[] = +{ + {"loginuid-immutable", 0, NULL, 1}, + {"backlog_wait_time", 1, NULL, 2}, + {NULL, 0, NULL, 0} +}; + +// FIXME: Change these to enums +/* + * returns: -3 deprecated, -2 success - no reply, -1 error - noreply, + * 0 success - reply, > 0 success - rule + */ +static int setopt(int count, int lineno, char *vars[]) +{ + int c; + int retval = 0, rc; + + optind = 0; + opterr = 0; + key[0] = 0; + keylen = AUDIT_MAX_KEY_LEN; + + while ((retval >= 0) && (c = getopt_long(count, vars, + "hicslDvtC:e:f:r:b:a:A:d:S:F:m:R:w:W:k:p:q:", + long_opts, NULL)) != EOF) { + int flags = AUDIT_FILTER_UNSET; + rc = 10; // Init to something impossible to see if unused. + switch (c) { + case 'h': + usage(); + retval = -1; + break; + case 'i': + ignore = 1; + retval = -2; + break; + case 'c': + ignore = 1; + continue_error = 1; + retval = -2; + break; + case 's': + if (count > 3) { + audit_msg(LOG_ERR, + "Too many options for status command"); + retval = -1; + break; + } else if (optind == 2 && count == 3) { + if (strcmp(vars[optind], "-i") == 0) { + interpret = 1; + count -= 1; + } else { + audit_msg(LOG_ERR, + "Only -i option is allowed"); + retval = -1; + break; + } + } + retval = report_status(fd); + break; + case 'e': + if (optarg && ((strcmp(optarg, "0") == 0) || + (strcmp(optarg, "1") == 0) || + (strcmp(optarg, "2") == 0))) { + if (audit_set_enabled(fd, strtoul(optarg,NULL,0)) > 0) + audit_request_status(fd); + else + retval = -1; + } else { + audit_msg(LOG_ERR, "Enable must be 0, 1, or 2 was %s", + optarg); + retval = -1; + } + break; + case 'f': + if (optarg && ((strcmp(optarg, "0") == 0) || + (strcmp(optarg, "1") == 0) || + (strcmp(optarg, "2") == 0))) { + if (audit_set_failure(fd, strtoul(optarg,NULL,0)) > 0) + audit_request_status(fd); + else + return -1; + } else { + audit_msg(LOG_ERR, "Failure must be 0, 1, or 2 was %s", + optarg); + retval = -1; + } + break; + case 'r': + if (optarg && isdigit(optarg[0])) { + uint32_t rate; + errno = 0; + rate = strtoul(optarg,NULL,0); + if (errno) { + audit_msg(LOG_ERR, "Error converting rate"); + return -1; + } + if (audit_set_rate_limit(fd, rate) > 0) + audit_request_status(fd); + else + return -1; + } else { + audit_msg(LOG_ERR,"Rate must be a numeric value was %s", + optarg); + retval = -1; + } + break; + case 'b': + if (optarg && isdigit(optarg[0])) { + uint32_t limit; + errno = 0; + limit = strtoul(optarg,NULL,0); + if (errno) { + audit_msg(LOG_ERR, "Error converting backlog"); + return -1; + } + if (audit_set_backlog_limit(fd, limit) > 0) + audit_request_status(fd); + else + return -1; + } else { + audit_msg(LOG_ERR, + "Backlog must be a numeric value was %s", + optarg); + retval = -1; + } + break; + case 'l': + if (count > 4) { + audit_msg(LOG_ERR, + "Wrong number of options for list request"); + retval = -1; + break; + } + if (count == 3) { + if (strcmp(vars[optind], "-i") == 0) { + interpret = 1; + count -= 1; + } else { + audit_msg(LOG_ERR, + "Only -k or -i options are allowed"); + retval = -1; + } + } else if (count == 4) { + if (vars[optind] && strcmp(vars[optind], "-k") == 0) { + strncat(key, vars[3], keylen); + count -= 2; + } else { + audit_msg(LOG_ERR, + "Only -k or -i options are allowed"); + retval = -1; + break; + } + } + if (audit_request_rule_list(fd)) { + list_requested = 1; + retval = -2; + } else + retval = -1; + break; + case 'a': + if (strstr(optarg, "task") && _audit_syscalladded) { + audit_msg(LOG_ERR, + "Syscall auditing requested for task list"); + retval = -1; + } else { + rc = audit_rule_setup(optarg, &add, &action, lineno); + if (rc == 3) { + audit_msg(LOG_ERR, + "Multiple rule insert/delete operations are not allowed\n"); + retval = -1; + } else if (rc == 2) { + audit_msg(LOG_ERR, + "Append rule - bad keyword %s", + optarg); + retval = -1; + } else if (rc == 1) { + audit_msg(LOG_ERR, + "Append rule - possible is deprecated"); + return -3; /* deprecated - eat it */ + } else + retval = 1; /* success - please send */ + } + break; + case 'A': + if (strstr(optarg, "task") && _audit_syscalladded) { + audit_msg(LOG_ERR, + "Error: syscall auditing requested for task list"); + retval = -1; + } else { + rc = audit_rule_setup(optarg, &add, &action, lineno); + if (rc == 3) { + audit_msg(LOG_ERR, + "Multiple rule insert/delete operations are not allowed"); + retval = -1; + } else if (rc == 2) { + audit_msg(LOG_ERR, + "Add rule - bad keyword %s", optarg); + retval = -1; + } else if (rc == 1) { + audit_msg(LOG_WARNING, + "Append rule - possible is deprecated"); + return -3; /* deprecated - eat it */ + } else { + add |= AUDIT_FILTER_PREPEND; + retval = 1; /* success - please send */ + } + } + break; + case 'd': + rc = audit_rule_setup(optarg, &del, &action, lineno); + if (rc == 3) { + audit_msg(LOG_ERR, + "Multiple rule insert/delete operations are not allowed"); + retval = -1; + } else if (rc == 2) { + audit_msg(LOG_ERR, "Delete rule - bad keyword %s", + optarg); + retval = -1; + } else if (rc == 1) { + audit_msg(LOG_INFO, + "Delete rule - possible is deprecated"); + return -3; /* deprecated - eat it */ + } else + retval = 1; /* success - please send */ + break; + case 'S': { + int unknown_arch = !_audit_elf; + /* Do some checking to make sure that we are not adding a + * syscall rule to a list that does not make sense. */ + if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == + AUDIT_FILTER_TASK || (del & + (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == + AUDIT_FILTER_TASK)) { + audit_msg(LOG_ERR, + "Error: syscall auditing being added to task list"); + return -1; + } else if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == + AUDIT_FILTER_USER || (del & + (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == + AUDIT_FILTER_USER)) { + audit_msg(LOG_ERR, + "Error: syscall auditing being added to user list"); + return -1; + } else if (exclude) { + audit_msg(LOG_ERR, + "Error: syscall auditing cannot be put on exclude list"); + return -1; + } else { + if (unknown_arch) { + int machine; + unsigned int elf; + machine = audit_detect_machine(); + if (machine < 0) { + audit_msg(LOG_ERR, + "Error detecting machine type"); + return -1; + } + elf = audit_machine_to_elf(machine); + if (elf == 0) { + audit_msg(LOG_ERR, + "Error looking up elf type %d", + machine); + return -1; + } + _audit_elf = elf; + } + } + rc = parse_syscall(rule_new, optarg); + switch (rc) + { + case 0: + _audit_syscalladded = 1; + if (unknown_arch && add != AUDIT_FILTER_UNSET) + check_rule_mismatch(lineno, optarg); + break; + case -1: + audit_msg(LOG_ERR, "Syscall name unknown: %s", + optarg); + retval = -1; + break; + case -2: + audit_msg(LOG_ERR, "Elf type unknown: 0x%x", + _audit_elf); + retval = -1; + break; + case -3: // Error reported - do nothing here + retval = -1; + break; + }} + break; + case 'F': + if (add != AUDIT_FILTER_UNSET) + flags = add & AUDIT_FILTER_MASK; + else if (del != AUDIT_FILTER_UNSET) + flags = del & AUDIT_FILTER_MASK; + // if the field is arch & there is a -t option...we + // can allow it + else if ((optind >= count) || (strstr(optarg, "arch=") == NULL) + || (strcmp(vars[optind], "-t") != 0)) { + audit_msg(LOG_ERR, "List must be given before field"); + retval = -1; + break; + } + + rc = audit_rule_fieldpair_data(&rule_new,optarg,flags); + if (rc != 0) { + audit_number_to_errmsg(rc, optarg); + retval = -1; + } else { + if (rule_new->fields[rule_new->field_count-1] == + AUDIT_PERM) + _audit_permadded = 1; + } + + break; + case 'C': + if (add != AUDIT_FILTER_UNSET) + flags = add & AUDIT_FILTER_MASK; + else if (del != AUDIT_FILTER_UNSET) + flags = del & AUDIT_FILTER_MASK; + + rc = audit_rule_interfield_comp_data(&rule_new, optarg, flags); + if (rc != 0) { + audit_number_to_errmsg(rc, optarg); + retval = -1; + } else { + if (rule_new->fields[rule_new->field_count - 1] == + AUDIT_PERM) + _audit_permadded = 1; + } + break; + case 'm': + if (count > 3) { + audit_msg(LOG_ERR, + "The -m option must be only the only option and takes 1 parameter"); + retval = -1; + } else { + const char*s = optarg; + while (*s) { + if (*s < 32) { + audit_msg(LOG_ERR, + "Illegal character in audit event"); + return -1; + } + s++; + } + if (audit_log_user_message( fd, AUDIT_USER, + optarg, NULL, NULL, NULL, 1) <= 0) + retval = -1; + else + return -2; // success - no reply for this + } + break; + case 'R': + audit_msg(LOG_ERR, "Error - nested rule files not supported"); + retval = -1; + break; + case 'D': + if (count > 4 || count == 3) { + audit_msg(LOG_ERR, + "Wrong number of options for Delete all request"); + retval = -1; + break; + } + if (count == 4) { + if (strcmp(vars[optind], "-k") == 0) { + strncat(key, vars[3], keylen); + count -= 2; + } else { + audit_msg(LOG_ERR, + "Only the -k option is allowed"); + retval = -1; + break; + } + } + retval = delete_all_rules(fd); + if (retval == 0) { + (void)audit_request_rule_list(fd); + key[0] = 0; + retval = -2; + } + break; + case 'w': + if (add != AUDIT_FILTER_UNSET || + del != AUDIT_FILTER_UNSET) { + audit_msg(LOG_ERR, + "watch option can't be given with a syscall"); + retval = -1; + } else if (optarg) { + add = AUDIT_FILTER_EXIT; + action = AUDIT_ALWAYS; + _audit_syscalladded = 1; + retval = audit_setup_watch_name(&rule_new, optarg); + } else { + audit_msg(LOG_ERR, "watch option needs a path"); + retval = -1; + } + break; + case 'W': + if (optarg) { + del = AUDIT_FILTER_EXIT; + action = AUDIT_ALWAYS; + _audit_syscalladded = 1; + retval = audit_setup_watch_name(&rule_new, optarg); + } else { + audit_msg(LOG_ERR, "watch option needs a path"); + retval = -1; + } + break; + case 'k': + if (!(_audit_syscalladded || _audit_permadded ) || + (add==AUDIT_FILTER_UNSET && + del==AUDIT_FILTER_UNSET)) { + audit_msg(LOG_ERR, + "key option needs a watch or syscall given prior to it"); + retval = -1; + } else if (!optarg) { + audit_msg(LOG_ERR, "key option needs a value"); + retval = -1; + } else if ((strlen(optarg)+strlen(key)+(!!key[0])) > + AUDIT_MAX_KEY_LEN) { + audit_msg(LOG_ERR, "key option exceeds size limit"); + retval = -1; + } else { + if (strncmp(optarg, "ids-", 4) == 0) { + if (check_ids_key(optarg)) { + retval = -1; + break; + } + } + if (strchr(optarg, AUDIT_KEY_SEPARATOR)) + audit_msg(LOG_ERR, + "key %s has illegal character", optarg); + if (key[0]) { // Add the separator if we need to + strcat(key, key_sep); + keylen--; + } + strncat(key, optarg, keylen); + keylen = AUDIT_MAX_KEY_LEN - strlen(key); + } + break; + case 'p': + if (!add && !del) { + audit_msg(LOG_ERR, + "permission option needs a watch given prior to it"); + retval = -1; + } else if (!optarg) { + audit_msg(LOG_ERR, "permission option needs a filter"); + retval = -1; + } else + retval = audit_setup_perms(rule_new, optarg); + break; + case 'q': + if (_audit_syscalladded) { + audit_msg(LOG_ERR, + "Syscall auditing requested for make equivalent"); + retval = -1; + } else { + char *mp, *sub; + retval = equiv_parse(optarg, &mp, &sub); + if (retval < 0) { + audit_msg(LOG_ERR, + "Error parsing equivalent parts"); + retval = -1; + } else { + retval = audit_make_equivalent(fd, mp, sub); + if (retval <= 0) { + retval = -1; + } else + return -2; // success - no reply needed + } + } + break; + case 't': + retval = audit_trim_subtrees(fd); + if (retval <= 0) + retval = -1; + else + return -2; // success - no reply for this + break; + case 'v': + printf("auditctl version %s\n", VERSION); + retval = -2; + break; + // Now the long options + case 1: + retval = audit_set_loginuid_immutable(fd); + if (retval <= 0) + retval = -1; + else + return -2; // success - no reply for this + break; + case 2: +#if HAVE_DECL_AUDIT_VERSION_BACKLOG_WAIT_TIME + if (optarg && isdigit(optarg[0])) { + uint32_t bwt; + errno = 0; + bwt = strtoul(optarg,NULL,0); + if (errno) { + audit_msg(LOG_ERR, + "Error converting backlog_wait_time"); + return -1; + } + if (audit_set_backlog_wait_time(fd, bwt) > 0) + audit_request_status(fd); + else + return -1; + } else { + audit_msg(LOG_ERR, + "Backlog_wait_time must be a numeric value was %s", + optarg); + retval = -1; + } +#else + audit_msg(LOG_ERR, + "backlog_wait_time is not supported on your kernel"); + retval = -1; +#endif + break; + default: + usage(); + retval = -1; + break; + } + } + /* catch extra args or errors where the user types "- s" */ + if (optind == 1) + retval = -1; + else if ((optind < count) && (retval != -1)) { + audit_msg(LOG_ERR, "parameter passed without an option given"); + retval = -1; + } + + /* See if we were adding a key */ + if (key[0] && list_requested == 0) { + int flags = 0; + char *cmd=NULL; + + /* Get the flag */ + if (add != AUDIT_FILTER_UNSET) + flags = add & AUDIT_FILTER_MASK; + else if (del != AUDIT_FILTER_UNSET) + flags = del & AUDIT_FILTER_MASK; + + /* Build the command */ + if (asprintf(&cmd, "key=%s", key) < 0) { + cmd = NULL; + audit_msg(LOG_ERR, "Out of memory adding key"); + retval = -1; + } else { + /* Add this to the rule */ + int ret = audit_rule_fieldpair_data(&rule_new, cmd, flags); + if (ret < 0) + retval = -1; + free(cmd); + } + } + if (retval == -1 && errno == ECONNREFUSED) + audit_msg(LOG_ERR, "The audit system is disabled"); + return retval; +} + +static char *get_line(FILE *f, char *buf) +{ + if (fgets_unlocked(buf, LINE_SIZE, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) + *ptr = 0; + return buf; + } + return NULL; +} + + +void preprocess(char *buf) +{ + unsigned int i = 0; + bool esc_ctx = false; + + while (buf[i]) { + if (buf[i] == '\\' && esc_ctx == false) + esc_ctx = true; + else { + if (esc_ctx == true) { + if (buf[i] == ' ') { + buf[i] = 0x07; + buf[i - 1] = 0x07; + } else if (buf[i] == '\\') { + buf[i] = 0x04; + buf[i - 1] = 0x04; + } + + esc_ctx = false; + } + } + + i++; + } +} + + +void postprocess(char *buf) +{ + char *str = strdup(buf); + char *pos1 = str; + char *pos2 = buf; + + if (!str) + return; + + while (*pos1) { + if (*pos1 == 0x07) { + *pos2 = ' '; + pos1 += 2; + pos2++; + continue; + } else if (*pos1 == 0x04) { + *pos2 = '\\'; + pos1 += 2; + pos2++; + continue; + } + + *pos2 = *pos1; + pos2++; + pos1++; + } + + *pos2 = 0; + free(str); +} + + +/* + * This function reads the given file line by line and executes the rule. + * It returns 0 if everything went OK, 1 if there are problems before reading + * the file and -1 on error conditions after executing some of the rules. + * It will abort reading the file if it encounters any problems. + */ +static int fileopt(const char *file) +{ + int i, tfd, rc, lineno = 1; + struct stat st; + FILE *f; + char buf[LINE_SIZE]; + + /* Does the file exist? */ + rc = open(file, O_RDONLY); + if (rc < 0) { + if (errno != ENOENT) { + audit_msg(LOG_ERR,"Error opening %s (%s)", + file, strerror(errno)); + return 1; + } + audit_msg(LOG_INFO, "file %s doesn't exist, skipping", file); + return 0; + } + tfd = rc; + + /* Is the file permissions sane? */ + if (fstat(tfd, &st) < 0) { + audit_msg(LOG_ERR, "Error fstat'ing %s (%s)", + file, strerror(errno)); + close(tfd); + return 1; + } + if (st.st_uid != 0) { + audit_msg(LOG_ERR, "Error - %s isn't owned by root", file); + close(tfd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + audit_msg(LOG_ERR, "Error - %s is world writable", file); + close(tfd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + audit_msg(LOG_ERR, "Error - %s is not a regular file", file); + close(tfd); + return 1; + } + + f = fdopen(tfd, "rm"); + if (f == NULL) { + audit_msg(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(tfd); + return 1; + } + + /* Read until eof, lineno starts as 1 */ + while (get_line(f, buf)) { + char *ptr, **fields; + int idx=0, nf = (strlen(buf)/3) + 3; + + /* Weed out blank lines */ + while (buf[idx] == ' ') + idx++; + if (buf[idx] == 0) { + lineno++; + continue; + } + + preprocess(buf); + ptr = audit_strsplit(buf); + if (ptr == NULL) + break; + + /* allow comments */ + if (ptr[0] == '#') { + lineno++; + continue; + } + i = 0; + fields = malloc(nf * sizeof(char *)); + fields[i++] = "auditctl"; + fields[i++] = ptr; + while( (ptr=audit_strsplit(NULL)) && (i < nf-1)) { + postprocess(ptr); + fields[i++] = ptr; + } + + fields[i] = NULL; + + /* Parse it */ + if (reset_vars()) { + free(fields); + fclose(f); + return -1; + } + rc = setopt(i, lineno, fields); + free(fields); + + /* handle reply or send rule */ + if (rc != -3) { + if (handle_request(rc) == -1) { + if (errno != ECONNREFUSED) + audit_msg(LOG_ERR, + "There was an error in line %d of %s", + lineno, file); + else { + audit_msg(LOG_ERR, + "The audit system is disabled"); + fclose(f); + return 0; + } + if (ignore == 0) { + fclose(f); + return -1; + } + if (continue_error) + continue_error = -1; + } + } + lineno++; + } + fclose(f); + return 0; +} + +/* Return 1 if ready, 0 otherwise */ +static int is_ready(int fd) +{ + if (audit_is_enabled(fd) == 2) { + audit_msg(LOG_ERR, "The audit system is in immutable mode," + " no rule changes allowed"); + return 0; + } else if (errno == ECONNREFUSED) { + audit_msg(LOG_ERR, "The audit system is disabled"); + return 0; + } + return 1; +} + +int main(int argc, char *argv[]) +{ + int retval = 1; + + set_aumessage_mode(MSG_STDERR, DBG_NO); + + if (argc == 1) { + usage(); + return 1; + } +#ifndef DEBUG + /* Make sure we are root if we do anything except help */ + if (!(argc == 2 && (strcmp(argv[1], "--help")==0 || + strcmp(argv[1], "-h") == 0)) && (geteuid() != 0)) { + audit_msg(LOG_WARNING, "You must be root to run this program."); + return 4; + } +#endif + /* Check where the rules are coming from: commandline or file */ + if ((argc == 3) && (strcmp(argv[1], "-R") == 0)) { + // If reading a file, its most likely start up. Send problems + // to syslog where they will persist for later review + set_aumessage_mode(MSG_SYSLOG, DBG_NO); + fd = audit_open(); + if (is_ready(fd) == 0) + return 0; + else if (fileopt(argv[2])) { + free(rule_new); + return 1; + } else { + free(rule_new); + if (continue_error < 0) + return 1; + return 0; + } + } else { + if (reset_vars()) { + free(rule_new); + return 1; + } + retval = setopt(argc, 0, argv); + if (retval == -3) { + free(rule_new); + return 0; + } + } + + if (add != AUDIT_FILTER_UNSET || del != AUDIT_FILTER_UNSET) { + fd = audit_open(); + if (is_ready(fd) == 0) { + free(rule_new); + return 0; + } + } + retval = handle_request(retval); + free(rule_new); + return retval; +} + +/* + * This function is called after setopt to handle the return code. + * On entry, status = 0 means just get the reply. Greater than 0 means we + * are adding or deleting a rule or watch. -1 means an error occurred. + * -2 means everything is OK and no reply needed. Even if there's an + * error, we need to call this routine to close up the audit fd. + * The return code from this function is 0 success and -1 error. + */ +static int handle_request(int status) +{ + if (status == 0) { + if (_audit_syscalladded) { + audit_msg(LOG_ERR, "Error - no list specified"); + return -1; + } + get_reply(); + } else if (status == -2) + status = 0; // report success + else if (status > 0) { + int rc; + if (add != AUDIT_FILTER_UNSET) { + // if !task add syscall any if not specified + if ((add & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK && + _audit_syscalladded != 1) { + audit_rule_syscallbyname_data( + rule_new, "all"); + } + set_aumessage_mode(MSG_QUIET, DBG_NO); + rc = audit_add_rule_data(fd, rule_new, add, action); + set_aumessage_mode(MSG_STDERR, DBG_NO); + /* Retry for legacy kernels */ + if (rc < 0) { + if (errno == EINVAL && + rule_new->fields[0] == AUDIT_DIR) { + rule_new->fields[0] = AUDIT_WATCH; + rc = audit_add_rule_data(fd, rule_new, + add, action); + } else { + audit_msg(LOG_ERR, + "Error sending add rule data request (%s)", + errno == EEXIST ? + "Rule exists" : strerror(-rc)); + } + } + } + else if (del != AUDIT_FILTER_UNSET) { + if ((del & AUDIT_FILTER_MASK) != AUDIT_FILTER_TASK && + _audit_syscalladded != 1) { + audit_rule_syscallbyname_data( + rule_new, "all"); + } + set_aumessage_mode(MSG_QUIET, DBG_NO); + rc = audit_delete_rule_data(fd, rule_new, + del, action); + set_aumessage_mode(MSG_STDERR, DBG_NO); + /* Retry for legacy kernels */ + if (rc < 0) { + if (errno == EINVAL && + rule_new->fields[0] == AUDIT_DIR) { + rule_new->fields[0] = AUDIT_WATCH; + rc = audit_delete_rule_data(fd,rule_new, + del, action); + } else { + audit_msg(LOG_ERR, + "Error sending delete rule data request (%s)", + errno == EEXIST ? + "Rule exists" : strerror(-rc)); + } + } + } else { + usage(); + audit_close(fd); + exit(1); + } + if (rc <= 0) + status = -1; + else + status = 0; + } else + status = -1; + + if (!list_requested) + audit_close(fd); + fd = -1; + return status; +} + +/* + * A reply from the kernel is expected. Get and display it. + */ +static void get_reply(void) +{ + int i, retval; + int timeout = 40; /* loop has delay of .1 - so this is 4 seconds */ + struct audit_reply rep; + fd_set read_mask; + FD_ZERO(&read_mask); + FD_SET(fd, &read_mask); + + // Reset printing counter + audit_print_init(); + + for (i = 0; i < timeout; i++) { + struct timeval t; + + t.tv_sec = 0; + t.tv_usec = 100000; /* .1 second */ + do { + retval=select(fd+1, &read_mask, NULL, NULL, &t); + } while (retval < 0 && errno == EINTR); + // We'll try to read just in case + retval = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + if (retval > 0) { + if (rep.type == NLMSG_ERROR && rep.error->error == 0) { + i = 0; /* reset timeout */ + continue; /* This was an ack */ + } + + if ((retval = audit_print_reply(&rep, fd)) == 0) + break; + else + i = 0; /* If getting more, reset timeout */ + } + } +} + diff --git a/framework/src/audit/src/auditd-config.c b/framework/src/audit/src/auditd-config.c new file mode 100644 index 00000000..0a99ffe0 --- /dev/null +++ b/framework/src/audit/src/auditd-config.c @@ -0,0 +1,1744 @@ +/* auditd-config.c -- + * Copyright 2004-2011,2013-14 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* O_NOFOLLOW needs gnu defined */ +#include +#include +#include /* INT_MAX */ +#include "auditd-config.h" +#include "libaudit.h" +#include "private.h" + +#define TCP_PORT_MAX 65535 + +/* Local prototypes */ +struct nv_pair +{ + const char *name; + const char *value; + const char *option; +}; + +struct kw_pair +{ + const char *name; + int (*parser)(struct nv_pair *, int, struct daemon_conf *); + int max_options; +}; + +struct nv_list +{ + const char *name; + int option; +}; + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file); +static int nv_split(char *buf, struct nv_pair *nv); +static const struct kw_pair *kw_lookup(const char *val); +static int log_file_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int num_logs_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int log_group_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int qos_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int dispatch_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int name_format_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int name_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int max_log_size_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int max_log_size_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int log_format_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int flush_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int freq_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int space_left_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int space_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int action_mail_acct_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int admin_space_left_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int admin_space_left_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int disk_full_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int disk_error_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int priority_boost_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int tcp_listen_port_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int tcp_listen_queue_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int tcp_max_per_addr_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int use_libwrap_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int tcp_client_ports_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int tcp_client_max_idle_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int enable_krb5_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int krb5_principal_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int krb5_key_file_parser(struct nv_pair *nv, int line, + struct daemon_conf *config); +static int sanity_check(struct daemon_conf *config); + +static const struct kw_pair keywords[] = +{ + {"log_file", log_file_parser, 0 }, + {"log_format", log_format_parser, 0 }, + {"log_group", log_group_parser, 0 }, + {"flush", flush_parser, 0 }, + {"freq", freq_parser, 0 }, + {"num_logs", num_logs_parser, 0 }, + {"dispatcher", dispatch_parser, 0 }, + {"name_format", name_format_parser, 0 }, + {"name", name_parser, 0 }, + {"disp_qos", qos_parser, 0 }, + {"max_log_file", max_log_size_parser, 0 }, + {"max_log_file_action", max_log_size_action_parser, 0 }, + {"space_left", space_left_parser, 0 }, + {"space_left_action", space_action_parser, 1 }, + {"action_mail_acct", action_mail_acct_parser, 0 }, + {"admin_space_left", admin_space_left_parser, 0 }, + {"admin_space_left_action", admin_space_left_action_parser, 1 }, + {"disk_full_action", disk_full_action_parser, 1 }, + {"disk_error_action", disk_error_action_parser, 1 }, + {"priority_boost", priority_boost_parser, 0 }, + {"tcp_listen_port", tcp_listen_port_parser, 0 }, + {"tcp_listen_queue", tcp_listen_queue_parser, 0 }, + {"tcp_max_per_addr", tcp_max_per_addr_parser, 0 }, + {"use_libwrap", use_libwrap_parser, 0 }, + {"tcp_client_ports", tcp_client_ports_parser, 0 }, + {"tcp_client_max_idle", tcp_client_max_idle_parser, 0 }, + {"enable_krb5", enable_krb5_parser, 0 }, + {"krb5_principal", krb5_principal_parser, 0 }, + {"krb5_key_file", krb5_key_file_parser, 0 }, + { NULL, NULL } +}; + +static const struct nv_list log_formats[] = +{ + {"raw", LF_RAW }, + {"nolog", LF_NOLOG }, + { NULL, 0 } +}; + +static const struct nv_list flush_techniques[] = +{ + {"none", FT_NONE }, + {"incremental", FT_INCREMENTAL }, + {"data", FT_DATA }, + {"sync", FT_SYNC }, + { NULL, 0 } +}; + +static const struct nv_list failure_actions[] = +{ + {"ignore", FA_IGNORE }, + {"syslog", FA_SYSLOG }, + {"rotate", FA_ROTATE }, + {"email", FA_EMAIL }, + {"exec", FA_EXEC }, + {"suspend", FA_SUSPEND }, + {"single", FA_SINGLE }, + {"halt", FA_HALT }, + { NULL, 0 } +}; + +// Future ideas: e-mail, run command +static const struct nv_list size_actions[] = +{ + {"ignore", SZ_IGNORE }, + {"syslog", SZ_SYSLOG }, + {"suspend", SZ_SUSPEND }, + {"rotate", SZ_ROTATE }, + {"keep_logs", SZ_KEEP_LOGS}, + { NULL, 0 } +}; + +static const struct nv_list qos_options[] = +{ + {"lossy", QOS_NON_BLOCKING }, + {"lossless", QOS_BLOCKING }, + { NULL, 0 } +}; + +static const struct nv_list node_name_formats[] = +{ + {"none", N_NONE }, + {"hostname", N_HOSTNAME }, + {"fqd", N_FQD }, + {"numeric", N_NUMERIC }, + {"user", N_USER }, + { NULL, 0 } +}; + +static const struct nv_list yes_no_values[] = +{ + {"yes", 1 }, + {"no", 0 }, + { NULL, 0 } +}; + +const char *email_command = "/usr/lib/sendmail"; +static int allow_links = 0; + + +void set_allow_links(int allow) +{ + allow_links = allow; +} + +/* + * Set everything to its default value +*/ +void clear_config(struct daemon_conf *config) +{ + config->qos = QOS_NON_BLOCKING; + config->sender_uid = 0; + config->sender_pid = 0; + config->sender_ctx = NULL; + config->log_file = strdup("/var/log/audit/audit.log"); + config->log_format = LF_RAW; + config->log_group = 0; + config->priority_boost = 4; + config->flush = FT_NONE; + config->freq = 0; + config->num_logs = 0L; + config->dispatcher = NULL; + config->node_name_format = N_NONE; + config->node_name = NULL; + config->max_log_size = 0L; + config->max_log_size_action = SZ_IGNORE; + config->space_left = 0L; + config->space_left_action = FA_IGNORE; + config->space_left_exe = NULL; + config->action_mail_acct = strdup("root"); + config->admin_space_left= 0L; + config->admin_space_left_action = FA_IGNORE; + config->admin_space_left_exe = NULL; + config->disk_full_action = FA_IGNORE; + config->disk_full_exe = NULL; + config->disk_error_action = FA_SYSLOG; + config->disk_error_exe = NULL; + config->tcp_listen_port = 0; + config->tcp_listen_queue = 5; + config->tcp_max_per_addr = 1; + config->use_libwrap = 1; + config->tcp_client_min_port = 0; + config->tcp_client_max_port = TCP_PORT_MAX; + config->tcp_client_max_idle = 0; + config->enable_krb5 = 0; + config->krb5_principal = NULL; + config->krb5_key_file = NULL; +} + +static log_test_t log_test = TEST_AUDITD; +int load_config(struct daemon_conf *config, log_test_t lt) +{ + int fd, rc, mode, lineno = 1; + struct stat st; + FILE *f; + char buf[160]; + + clear_config(config); + log_test = lt; + + /* open the file */ + mode = O_RDONLY; + if (allow_links == 0) + mode |= O_NOFOLLOW; + rc = open(CONFIG_FILE, mode); + if (rc < 0) { + if (errno != ENOENT) { + audit_msg(LOG_ERR, "Error opening config file (%s)", + strerror(errno)); + return 1; + } + audit_msg(LOG_WARNING, + "Config file %s doesn't exist, skipping", CONFIG_FILE); + return 0; + } + fd = rc; + + /* check the file's permissions: owned by root, not world writable, + * not symlink. + */ + audit_msg(LOG_DEBUG, "Config file %s opened for parsing", + CONFIG_FILE); + if (fstat(fd, &st) < 0) { + audit_msg(LOG_ERR, "Error fstat'ing config file (%s)", + strerror(errno)); + close(fd); + return 1; + } + if (st.st_uid != 0) { + audit_msg(LOG_ERR, "Error - %s isn't owned by root", + CONFIG_FILE); + close(fd); + return 1; + } + if ((st.st_mode & S_IWOTH) == S_IWOTH) { + audit_msg(LOG_ERR, "Error - %s is world writable", + CONFIG_FILE); + close(fd); + return 1; + } + if (!S_ISREG(st.st_mode)) { + audit_msg(LOG_ERR, "Error - %s is not a regular file", + CONFIG_FILE); + close(fd); + return 1; + } + + /* it's ok, read line by line */ + f = fdopen(fd, "rm"); + if (f == NULL) { + audit_msg(LOG_ERR, "Error - fdopen failed (%s)", + strerror(errno)); + close(fd); + return 1; + } + + while (get_line(f, buf, sizeof(buf), &lineno, CONFIG_FILE)) { + // convert line into name-value pair + const struct kw_pair *kw; + struct nv_pair nv; + rc = nv_split(buf, &nv); + switch (rc) { + case 0: // fine + break; + case 1: // not the right number of tokens. + audit_msg(LOG_ERR, + "Wrong number of arguments for line %d in %s", + lineno, CONFIG_FILE); + break; + case 2: // no '=' sign + audit_msg(LOG_ERR, + "Missing equal sign for line %d in %s", + lineno, CONFIG_FILE); + break; + default: // something else went wrong... + audit_msg(LOG_ERR, + "Unknown error for line %d in %s", + lineno, CONFIG_FILE); + break; + } + if (nv.name == NULL) { + lineno++; + continue; + } + if (nv.value == NULL) { + fclose(f); + audit_msg(LOG_ERR, + "Not processing any more lines in %s", + CONFIG_FILE); + return 1; + } + + /* identify keyword or error */ + kw = kw_lookup(nv.name); + if (kw->name == NULL) { + audit_msg(LOG_ERR, + "Unknown keyword \"%s\" in line %d of %s", + nv.name, lineno, CONFIG_FILE); + fclose(f); + return 1; + } + + /* Check number of options */ + if (kw->max_options == 0 && nv.option != NULL) { + audit_msg(LOG_ERR, + "Keyword \"%s\" has invalid option " + "\"%s\" in line %d of %s", + nv.name, nv.option, lineno, CONFIG_FILE); + fclose(f); + return 1; + } + + /* dispatch to keyword's local parser */ + rc = kw->parser(&nv, lineno, config); + if (rc != 0) { + fclose(f); + return 1; // local parser puts message out + } + + lineno++; + } + + fclose(f); + if (lineno > 1) + return sanity_check(config); + return 0; +} + +static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, + const char *file) +{ + int too_long = 0; + + while (fgets_unlocked(buf, size, f)) { + /* remove newline */ + char *ptr = strchr(buf, 0x0a); + if (ptr) { + if (!too_long) { + *ptr = 0; + return buf; + } + // Reset and start with the next line + too_long = 0; + *lineno = *lineno + 1; + } else { + // If a line is too long skip it. + // Only output 1 warning + if (!too_long) + audit_msg(LOG_ERR, + "Skipping line %d in %s: too long", + *lineno, file); + too_long = 1; + } + } + return NULL; +} + +static int nv_split(char *buf, struct nv_pair *nv) +{ + /* Get the name part */ + char *ptr; + + nv->name = NULL; + nv->value = NULL; + nv->option = NULL; + ptr = audit_strsplit(buf); + if (ptr == NULL) + return 0; /* If there's nothing, go to next line */ + if (ptr[0] == '#') + return 0; /* If there's a comment, go to next line */ + nv->name = ptr; + + /* Check for a '=' */ + ptr = audit_strsplit(NULL); + if (ptr == NULL) + return 1; + if (strcmp(ptr, "=") != 0) + return 2; + + /* get the value */ + ptr = audit_strsplit(NULL); + if (ptr == NULL) + return 1; + nv->value = ptr; + + /* See if there's an option */ + ptr = audit_strsplit(NULL); + if (ptr) { + nv->option = ptr; + + /* Make sure there's nothing else */ + ptr = audit_strsplit(NULL); + if (ptr) + return 1; + } + + /* Everything is OK */ + return 0; +} + +static const struct kw_pair *kw_lookup(const char *val) +{ + int i = 0; + while (keywords[i].name != NULL) { + if (strcasecmp(keywords[i].name, val) == 0) + break; + i++; + } + return &keywords[i]; +} + +static int log_file_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + char *dir = NULL, *tdir; + DIR *d; + int fd, mode; + struct stat buf; + + audit_msg(LOG_DEBUG, "log_file_parser called with: %s", nv->value); + + /* get dir from name. */ + tdir = strdup(nv->value); + if (tdir) + dir = dirname(tdir); + if (dir == NULL || strlen(dir) < 4) { // '/var' is shortest dirname + audit_msg(LOG_ERR, + "The directory name: %s is too short - line %d", + dir, line); + free((void *)tdir); + return 1; + } + + /* verify the directory path exists */ + d = opendir(dir); + if (d == NULL) { + audit_msg(LOG_ERR, "Could not open dir %s (%s)", dir, + strerror(errno)); + free((void *)tdir); + return 1; + } + free((void *)tdir); + closedir(d); + + /* if the file exists, see that its regular, owned by root, + * and not world anything */ + if (log_test == TEST_AUDITD) + mode = O_APPEND; + else + mode = O_RDONLY; + + fd = open(nv->value, mode); + if (fd < 0) { + if (errno == ENOENT) { + fd = create_log_file(nv->value); + if (fd < 0) + return 1; + } else { + audit_msg(LOG_ERR, "Unable to open %s (%s)", nv->value, + strerror(errno)); + return 1; + } + } + if (fstat(fd, &buf) < 0) { + audit_msg(LOG_ERR, "Unable to stat %s (%s)", + nv->value, strerror(errno)); + close(fd); + return 1; + } + close(fd); + if (!S_ISREG(buf.st_mode)) { + audit_msg(LOG_ERR, "%s is not a regular file", nv->value); + return 1; + } + if (buf.st_uid != 0) { + audit_msg(LOG_ERR, "%s is not owned by root", nv->value); + return 1; + } + if ( (buf.st_mode & (S_IXUSR|S_IWGRP|S_IXGRP|S_IRWXO)) ) { + audit_msg(LOG_ERR, "%s permissions should be 0600 or 0640", + nv->value); + return 1; + } + if ( !(buf.st_mode & S_IWUSR) ) { + audit_msg(LOG_ERR, "audit log is not writable by owner"); + return 1; + } + + free((void *)config->log_file); + config->log_file = strdup(nv->value); + if (config->log_file == NULL) + return 1; + return 0; +} + +static int num_logs_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "num_logs_parser called with: %s", nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + if (i > 99) { + audit_msg(LOG_ERR, "num_logs must be 99 or less"); + return 1; + } + config->num_logs = i; + return 0; +} + +static int qos_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "qos_parser called with: %s", nv->value); + for (i=0; qos_options[i].name != NULL; i++) { + if (strcasecmp(nv->value, qos_options[i].name) == 0) { + config->qos = qos_options[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int dispatch_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + char *dir = NULL, *tdir; + int fd; + struct stat buf; + + audit_msg(LOG_DEBUG, "dispatch_parser called with: %s", nv->value); + if (nv->value == NULL) { + config->dispatcher = NULL; + return 0; + } + + /* get dir from name. */ + tdir = strdup(nv->value); + if (tdir) + dir = dirname(tdir); + if (dir == NULL || strlen(dir) < 4) { // '/var' is shortest dirname + audit_msg(LOG_ERR, + "The directory name: %s is too short - line %d", + dir, line); + free(tdir); + return 1; + } + + free((void *)tdir); + + /* Bypass the perms check if group is not root since + * this will fail under normal circumstances */ + if ((config->log_group != 0 && getuid() != 0) || + (log_test == TEST_SEARCH)) + goto bypass; + + /* if the file exists, see that its regular, owned by root, + * and not world anything */ + fd = open(nv->value, O_RDONLY); + if (fd < 0) { + audit_msg(LOG_ERR, "Unable to open %s (%s)", nv->value, + strerror(errno)); + return 1; + } + if (fstat(fd, &buf) < 0) { + audit_msg(LOG_ERR, "Unable to stat %s (%s)", nv->value, + strerror(errno)); + close(fd); + return 1; + } + close(fd); + if (!S_ISREG(buf.st_mode)) { + audit_msg(LOG_ERR, "%s is not a regular file", nv->value); + return 1; + } + if (buf.st_uid != 0) { + audit_msg(LOG_ERR, "%s is not owned by root", nv->value); + return 1; + } + if ((buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != + (S_IRWXU|S_IRGRP|S_IXGRP) && + (buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != + (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) { + audit_msg(LOG_ERR, "%s permissions should be 0750 or 0755", + nv->value); + return 1; + } +bypass: + free((void *)config->dispatcher); + config->dispatcher = strdup(nv->value); + if (config->dispatcher == NULL) + return 1; + return 0; +} + +static int name_format_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "name_format_parser called with: %s", nv->value); + for (i=0; node_name_formats[i].name != NULL; i++) { + if (strcasecmp(nv->value, node_name_formats[i].name) == 0) { + config->node_name_format = node_name_formats[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int name_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + audit_msg(LOG_DEBUG, "name_parser called with: %s", nv->value); + if (nv->value == NULL) + config->node_name = NULL; + else + config->node_name = strdup(nv->value); + return 0; +} + +static int max_log_size_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "max_log_size_parser called with: %s", nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + config->max_log_size = i; + return 0; +} + +static int max_log_size_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "max_log_size_action_parser called with: %s", + nv->value); + for (i=0; size_actions[i].name != NULL; i++) { + if (strcasecmp(nv->value, size_actions[i].name) == 0) { + config->max_log_size_action = size_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int log_format_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "log_format_parser called with: %s", nv->value); + for (i=0; log_formats[i].name != NULL; i++) { + if (strcasecmp(nv->value, log_formats[i].name) == 0) { + config->log_format = log_formats[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int log_group_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + gid_t gid = 0; + + audit_msg(LOG_DEBUG, "log_group_parser called with: %s", + nv->value); + if (isdigit(nv->value[0])) { + errno = 0; + gid = strtoul(nv->value,NULL,10); + if (errno) { + audit_msg(LOG_ERR, + "Numeric group ID conversion error (%s) for %s - line %d\n", + strerror(errno), nv->value, line); + return 1; + } + } else { + struct group *gr ; + + gr = getgrnam(nv->value); + if (gr == NULL) { + audit_msg(LOG_ERR, + "Group ID is non-numeric and unknown (%s) - line %d\n", + nv->value, line); + return 1; + } + gid = gr->gr_gid; + } + config->log_group = gid; + return 0; +} + +static int flush_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "flush_parser called with: %s", nv->value); + for (i=0; flush_techniques[i].name != NULL; i++) { + if (strcasecmp(nv->value, flush_techniques[i].name) == 0) { + config->flush = flush_techniques[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int freq_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "freq_parser called with: %s", nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range */ + if (i > INT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + config->freq = (unsigned int)i; + return 0; +} + +static int space_left_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "space_left_parser called with: %s", nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + config->space_left = i; + return 0; +} + +static int check_exe_name(const char *val, int line) +{ + struct stat buf; + + if (val == NULL) { + audit_msg(LOG_ERR, "Executable path needed for line %d", line); + return -1; + } + + if (*val != '/') { + audit_msg(LOG_ERR, "Absolute path needed for %s - line %d", + val, line); + return -1; + } + + if (stat(val, &buf) < 0) { + audit_msg(LOG_ERR, "Unable to stat %s (%s) - line %d", val, + strerror(errno), line); + return -1; + } + if (!S_ISREG(buf.st_mode)) { + audit_msg(LOG_ERR, "%s is not a regular file - line %d", val, + line); + return -1; + } + if (buf.st_uid != 0) { + audit_msg(LOG_ERR, "%s is not owned by root - line %d", val, + line); + return -1; + } + if ((buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != + (S_IRWXU|S_IRGRP|S_IXGRP) && + (buf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != + (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) { + audit_msg(LOG_ERR, + "%s permissions should be 0750 or 0755 - line %d", + val, line); + return -1; + } + return 0; +} + +static int space_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "space_action_parser called with: %s", nv->value); + for (i=0; failure_actions[i].name != NULL; i++) { + if (strcasecmp(nv->value, failure_actions[i].name) == 0) { + if (failure_actions[i].option == FA_EMAIL) { + if (access(email_command, X_OK)) { + audit_msg(LOG_ERR, + "Email option is specified but %s doesn't seem executable.", + email_command); + } + } else if (failure_actions[i].option == FA_EXEC) { + if (check_exe_name(nv->option, line)) + return 1; + config->space_left_exe = strdup(nv->option); + } + config->space_left_action = failure_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +// returns 0 if OK, 1 on temp error, 2 on permanent error +static int validate_email(const char *acct) +{ + int i, len; + char *ptr1; + + if (acct == NULL) + return 2; + + len = strlen(acct); + if (len < 2) { + audit_msg(LOG_ERR, + "email: %s is too short, expecting at least 2 characters", + acct); + return 2; + } + + // look for illegal char + for (i=0; i ptr2)) { + audit_msg(LOG_ERR, "email: %s should have . after @", + acct); + return 2; + } + + t_addr = gethostbyname(ptr1+1); + if (t_addr == 0) { + if ((h_errno == HOST_NOT_FOUND) || + (h_errno == NO_RECOVERY)) { + audit_msg(LOG_ERR, + "validate_email: failed looking up host for %s", + ptr1+1); + // FIXME: gethostbyname is having trouble + // telling when we have a temporary vs permanent + // dns failure. So, for now, treat all as temp + return 1; + } + else if (h_errno == TRY_AGAIN) + audit_msg(LOG_DEBUG, + "validate_email: temporary failure looking up domain for %s", + ptr1+1); + return 1; + } + } + return 0; +} + +static int action_mail_acct_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + char *tmail; + + audit_msg(LOG_DEBUG, "action_mail_acct_parser called with: %s", + nv->value); + tmail = strdup(nv->value); + if (tmail == NULL) + return 1; + + if (validate_email(tmail) > 1) { + free(tmail); + return 1; + } + + + if (config->action_mail_acct) + free((void *)config->action_mail_acct); + config->action_mail_acct = tmail; + return 0; +} + +static int admin_space_left_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "admin_space_left_parser called with: %s", + nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned long */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + config->admin_space_left = i; + return 0; +} + +static int admin_space_left_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "admin_space_left_action_parser called with: %s", + nv->value); + for (i=0; failure_actions[i].name != NULL; i++) { + if (strcasecmp(nv->value, failure_actions[i].name) == 0) { + if (failure_actions[i].option == FA_EMAIL) { + if (access(email_command, X_OK)) { + audit_msg(LOG_ERR, + "Email option is specified but %s doesn't seem executable.", + email_command); + } + } else if (failure_actions[i].option == FA_EXEC) { + if (check_exe_name(nv->option, line)) + return 1; + config->admin_space_left_exe = + strdup(nv->option); + } + config->admin_space_left_action = + failure_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int disk_full_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "disk_full_action_parser called with: %s", + nv->value); + for (i=0; failure_actions[i].name != NULL; i++) { + if (strcasecmp(nv->value, failure_actions[i].name) == 0) { + if (failure_actions[i].option == FA_EMAIL) { + audit_msg(LOG_ERR, + "Illegal option %s for disk_full_action - line %d", + nv->value, line); + return 1; + } else if (failure_actions[i].option == FA_EXEC) { + if (check_exe_name(nv->option, line)) + return 1; + config->disk_full_exe = strdup(nv->option); + } + config->disk_full_action = failure_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int disk_error_action_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + int i; + + audit_msg(LOG_DEBUG, "disk_error_action_parser called with: %s", + nv->value); + for (i=0; failure_actions[i].name != NULL; i++) { + if (strcasecmp(nv->value, failure_actions[i].name) == 0) { + if (failure_actions[i].option == FA_EMAIL || + failure_actions[i].option == FA_ROTATE) { + audit_msg(LOG_ERR, + "Illegal option %s for disk_error_action - line %d", + nv->value, line); + return 1; + } else if (failure_actions[i].option == FA_EXEC) { + if (check_exe_name(nv->option, line)) + return 1; + config->disk_error_exe = strdup(nv->option); + } + config->disk_error_action = failure_actions[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int priority_boost_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "priority_boost_parser called with: %s", + nv->value); + + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range */ + if (i > INT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + config->priority_boost = (unsigned int)i; + return 0; +} + +static int tcp_listen_port_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "tcp_listen_port_parser called with: %s", + nv->value); + +#ifndef USE_LISTENER + audit_msg(LOG_DEBUG, + "Listener support is not enabled, ignoring value at line %d", + line); + return 0; +#else + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range */ + if (i > TCP_PORT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + if (i < 1) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too small - line %d", + nv->value, line); + return 1; + } + config->tcp_listen_port = (unsigned int)i; + return 0; +#endif +} + +static int tcp_listen_queue_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "tcp_listen_queue_parser called with: %s", + nv->value); + +#ifndef USE_LISTENER + audit_msg(LOG_DEBUG, + "Listener support is not enabled, ignoring value at line %d", + line); + return 0; +#else + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range. While this value is technically + unlimited, it's limited by the kernel, and we limit it here + for sanity. */ + if (i > TCP_PORT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + if (i < 1) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too small - line %d", + nv->value, line); + return 1; + } + config->tcp_listen_queue = (unsigned int)i; + return 0; +#endif +} + + +static int tcp_max_per_addr_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "tcp_max_per_addr_parser called with: %s", + nv->value); + +#ifndef USE_LISTENER + audit_msg(LOG_DEBUG, + "Listener support is not enabled, ignoring value at line %d", + line); + return 0; +#else + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range. While this value is technically + unlimited, it's limited by the kernel, and we limit it here + for sanity. */ + if (i > 1024) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + if (i < 1) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too small - line %d", + nv->value, line); + return 1; + } + config->tcp_max_per_addr = (unsigned int)i; + return 0; +#endif +} + +static int use_libwrap_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + unsigned long i; + + audit_msg(LOG_DEBUG, "use_libwrap_parser called with: %s", + nv->value); + + for (i=0; yes_no_values[i].name != NULL; i++) { + if (strcasecmp(nv->value, yes_no_values[i].name) == 0) { + config->use_libwrap = yes_no_values[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +} + +static int tcp_client_ports_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i, minv, maxv; + const char *saw_dash = NULL; + + audit_msg(LOG_DEBUG, "tcp_listen_queue_parser called with: %s", + nv->value); + +#ifndef USE_LISTENER + audit_msg(LOG_DEBUG, + "Listener support is not enabled, ignoring value at line %d", + line); + return 0; +#else + /* check that all chars are numbers, with an optional inclusive '-'. */ + for (i=0; ptr[i]; i++) { + if (i > 0 && ptr[i] == '-' && ptr[i+1] != '\0') { + saw_dash = ptr + i; + continue; + } + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers, or " + "two numbers separated by a dash - line %d", + nv->value, line); + return 1; + } + } + for (; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers, or " + "two numbers separated by a dash - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + maxv = minv = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + if (saw_dash) { + maxv = strtoul(saw_dash + 1, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + } + /* Check their ranges. */ + if (minv > TCP_PORT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%ld) is too large - line %d", + minv, line); + return 1; + } + if (maxv > TCP_PORT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%ld) is too large - line %d", + maxv, line); + return 1; + } + if (minv > maxv) { + audit_msg(LOG_ERR, + "Error - converted range (%ld-%ld) is reversed - line %d", + minv, maxv, line); + return 1; + } + config->tcp_client_min_port = (unsigned int)minv; + config->tcp_client_max_port = (unsigned int)maxv; + return 0; +#endif +} + +static int tcp_client_max_idle_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + const char *ptr = nv->value; + unsigned long i; + + audit_msg(LOG_DEBUG, "tcp_client_max_idle_parser called with: %s", + nv->value); + +#ifndef USE_LISTENER + audit_msg(LOG_DEBUG, + "Listener support is not enabled, ignoring value at line %d", + line); + return 0; +#else + /* check that all chars are numbers */ + for (i=0; ptr[i]; i++) { + if (!isdigit(ptr[i])) { + audit_msg(LOG_ERR, + "Value %s should only be numbers - line %d", + nv->value, line); + return 1; + } + } + + /* convert to unsigned int */ + errno = 0; + i = strtoul(nv->value, NULL, 10); + if (errno) { + audit_msg(LOG_ERR, + "Error converting string to a number (%s) - line %d", + strerror(errno), line); + return 1; + } + /* Check its range. While this value is technically + unlimited, it's limited by the kernel, and we limit it here + for sanity. */ + if (i > INT_MAX) { + audit_msg(LOG_ERR, + "Error - converted number (%s) is too large - line %d", + nv->value, line); + return 1; + } + config->tcp_client_max_idle = (unsigned int)i; + return 0; +#endif +} + +static int enable_krb5_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + audit_msg(LOG_DEBUG, "enable_krb5_parser called with: %s", + nv->value); + +#ifndef USE_GSSAPI + audit_msg(LOG_DEBUG, + "GSSAPI support is not enabled, ignoring value at line %d", + line); + return 0; +#else + unsigned long i; + + for (i=0; yes_no_values[i].name != NULL; i++) { + if (strcasecmp(nv->value, yes_no_values[i].name) == 0) { + config->enable_krb5 = yes_no_values[i].option; + return 0; + } + } + audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); + return 1; +#endif +} + +static int krb5_principal_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + audit_msg(LOG_DEBUG,"krb5_principal_parser called with: %s",nv->value); +#ifndef USE_GSSAPI + audit_msg(LOG_DEBUG, + "GSSAPI support is not enabled, ignoring value at line %d", + line); +#else + config->krb5_principal = strdup(nv->value); +#endif + return 0; +} + +static int krb5_key_file_parser(struct nv_pair *nv, int line, + struct daemon_conf *config) +{ + audit_msg(LOG_DEBUG, "krb5_key_file_parser called with: %s", nv->value); +#ifndef USE_GSSAPI + audit_msg(LOG_DEBUG, + "GSSAPI support is not enabled, ignoring value at line %d", + line); +#else + config->krb5_key_file = strdup(nv->value); +#endif + return 0; +} + +/* + * This function is where we do the integrated check of the audit config + * options. At this point, all fields have been read. Returns 0 if no + * problems and 1 if problems detected. + */ +static int sanity_check(struct daemon_conf *config) +{ + /* Error checking */ + if (config->space_left <= config->admin_space_left) { + audit_msg(LOG_ERR, + "Error - space_left(%lu) must be larger than admin_space_left(%lu)", + config->space_left, config->admin_space_left); + return 1; + } + if (config->flush == FT_INCREMENTAL && config->freq == 0) { + audit_msg(LOG_ERR, + "Error - incremental flushing chosen, but 0 selected for freq"); + return 1; + } + /* Warnings */ + if (config->flush > FT_INCREMENTAL && config->freq != 0) { + audit_msg(LOG_WARNING, + "Warning - freq is non-zero and incremental flushing not selected."); + } + return 0; +} + +const char *audit_lookup_format(int fmt) +{ + int i; + + for (i=0; log_formats[i].name != NULL; i++) { + if (log_formats[i].option == fmt) + return log_formats[i].name; + } + return NULL; +} + +int create_log_file(const char *val) +{ + int fd; + + umask(S_IRWXO); + fd = open(val, O_CREAT|O_EXCL|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP); + if (fd < 0) + audit_msg(LOG_ERR, "Unable to create %s (%s)", val, + strerror(errno)); + return fd; +} + +void free_config(struct daemon_conf *config) +{ + free((void *)config->sender_ctx); + free((void *)config->log_file); + free((void *)config->dispatcher); + free((void *)config->node_name); + free((void *)config->action_mail_acct); + free((void *)config->space_left_exe); + free((void *)config->admin_space_left_exe); + free((void *)config->disk_full_exe); + free((void *)config->disk_error_exe); + free((void *)config->krb5_principal); + free((void *)config->krb5_key_file); +} + +int resolve_node(struct daemon_conf *config) +{ + int rc = 0; + char tmp_name[255]; + + /* Get the host name representation */ + switch (config->node_name_format) + { + case N_NONE: + break; + case N_HOSTNAME: + if (gethostname(tmp_name, sizeof(tmp_name))) { + audit_msg(LOG_ERR, + "Unable to get machine name"); + rc = -1; + } else + config->node_name = strdup(tmp_name); + break; + case N_USER: + if (config->node_name == NULL) { + audit_msg(LOG_ERR, "User defined name missing"); + rc = -1; + } + break; + case N_FQD: + if (gethostname(tmp_name, sizeof(tmp_name))) { + audit_msg(LOG_ERR, + "Unable to get machine name"); + rc = -1; + } else { + int rc2; + struct addrinfo *ai; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + + rc2 = getaddrinfo(tmp_name, NULL, &hints, &ai); + if (rc2 != 0) { + audit_msg(LOG_ERR, + "Cannot resolve hostname %s (%s)", + tmp_name, gai_strerror(rc)); + rc = -1; + break; + } + config->node_name = strdup(ai->ai_canonname); + freeaddrinfo(ai); + } + break; + case N_NUMERIC: + if (gethostname(tmp_name, sizeof(tmp_name))) { + audit_msg(LOG_ERR, + "Unable to get machine name"); + rc = -1; + } else { + int rc2; + struct addrinfo *ai; + struct addrinfo hints; + + audit_msg(LOG_DEBUG, + "Resolving numeric address for %s", + tmp_name); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + + rc2 = getaddrinfo(tmp_name, NULL, &hints, &ai); + if (rc2) { + audit_msg(LOG_ERR, + "Cannot resolve hostname %s (%s)", + tmp_name, gai_strerror(rc2)); + rc = -1; + break; + } + inet_ntop(ai->ai_family, + ai->ai_family == AF_INET ? + (void *) &((struct sockaddr_in *)ai->ai_addr)->sin_addr : + (void *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, + tmp_name, INET6_ADDRSTRLEN); + freeaddrinfo(ai); + config->node_name = strdup(tmp_name); + } + break; + } + if (rc == 0 && config->node_name) + audit_msg(LOG_DEBUG, "Resolved node name: %s", + config->node_name); + return rc; +} + diff --git a/framework/src/audit/src/auditd-config.h b/framework/src/audit/src/auditd-config.h new file mode 100644 index 00000000..5a3eb6bb --- /dev/null +++ b/framework/src/audit/src/auditd-config.h @@ -0,0 +1,100 @@ +/* auditd-config.h -- + * Copyright 2004-2009,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUDITD_CONFIG_H +#define AUDITD_CONFIG_H + +#include "libaudit.h" +#include +#define CONFIG_FILE "/etc/audit/auditd.conf" +#define MEGABYTE 1048576UL + +typedef enum { D_FOREGROUND, D_BACKGROUND } daemon_t; +typedef enum { LF_RAW, LF_NOLOG } logging_formats; +typedef enum { FT_NONE, FT_INCREMENTAL, FT_DATA, FT_SYNC } flush_technique; +typedef enum { FA_IGNORE, FA_SYSLOG, FA_ROTATE, FA_EMAIL, FA_EXEC, FA_SUSPEND, + FA_SINGLE, FA_HALT } failure_action_t; +typedef enum { SZ_IGNORE, SZ_SYSLOG, SZ_SUSPEND, SZ_ROTATE, + SZ_KEEP_LOGS } size_action; +typedef enum { QOS_NON_BLOCKING, QOS_BLOCKING } qos_t; +typedef enum { TEST_AUDITD, TEST_SEARCH } log_test_t; +typedef enum { N_NONE, N_HOSTNAME, N_FQD, N_NUMERIC, N_USER } node_t; + +struct daemon_conf +{ + daemon_t daemonize; + qos_t qos; /* use blocking/non-blocking sockets */ + uid_t sender_uid; /* the uid for sender of sighup */ + pid_t sender_pid; /* the pid for sender of sighup */ + const char *sender_ctx; /* the context for the sender of sighup */ + const char *log_file; + logging_formats log_format; + gid_t log_group; + unsigned int priority_boost; + flush_technique flush; + unsigned int freq; + unsigned int num_logs; + const char *dispatcher; + node_t node_name_format; + const char *node_name; + unsigned long max_log_size; + size_action max_log_size_action; + unsigned long space_left; + failure_action_t space_left_action; + const char *space_left_exe; + const char *action_mail_acct; + unsigned long admin_space_left; + failure_action_t admin_space_left_action; + const char *admin_space_left_exe; + failure_action_t disk_full_action; + const char *disk_full_exe; + failure_action_t disk_error_action; + const char *disk_error_exe; + unsigned long tcp_listen_port; + unsigned long tcp_listen_queue; + unsigned long tcp_max_per_addr; + int use_libwrap; + unsigned long tcp_client_min_port; + unsigned long tcp_client_max_port; + unsigned long tcp_client_max_idle; + int enable_krb5; + const char *krb5_principal; + const char *krb5_key_file; +}; + +void set_allow_links(int allow); +int load_config(struct daemon_conf *config, log_test_t lt); +void clear_config(struct daemon_conf *config); +const char *audit_lookup_format(int fmt); +int create_log_file(const char *val); +int resolve_node(struct daemon_conf *config); + +void init_config_manager(void); +#ifdef AUDITD_EVENT_H +int start_config_manager(struct auditd_reply_list *rep); +#endif +void shutdown_config(void); +void free_config(struct daemon_conf *config); + +#endif + diff --git a/framework/src/audit/src/auditd-dispatch.c b/framework/src/audit/src/auditd-dispatch.c new file mode 100644 index 00000000..02681f03 --- /dev/null +++ b/framework/src/audit/src/auditd-dispatch.c @@ -0,0 +1,213 @@ +/* auditd-dispatch.c -- + * Copyright 2005-07,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Junji Kanemaru + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "libaudit.h" +#include "private.h" +#include "auditd-dispatch.h" + +/* This is the communications channel between auditd & the dispatcher */ +static int disp_pipe[2] = {-1, -1}; +static pid_t pid = 0; +static int n_errs = 0; +#define REPORT_LIMIT 10 + +int dispatcher_pid(void) +{ + return pid; +} + +void dispatcher_reaped(void) +{ + audit_msg(LOG_INFO, "dispatcher %d reaped\n", pid); + pid = 0; + shutdown_dispatcher(); +} + +/* set_flags: to set flags to file desc */ +static int set_flags(int fn, int flags) +{ + int fl; + + if ((fl = fcntl(fn, F_GETFL, 0)) < 0) { + audit_msg(LOG_ERR, "fcntl failed. Cannot get flags (%s)\n", + strerror(errno)); + return fl; + } + + fl |= flags; + + return fcntl(fn, F_SETFL, fl); +} + +/* This function returns 1 on error & 0 on success */ +int init_dispatcher(const struct daemon_conf *config) +{ + struct sigaction sa; + + if (config->dispatcher == NULL) + return 0; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, disp_pipe)) { + audit_msg(LOG_ERR, "Failed creating disp_pipe"); + return 1; + } + + /* Make both disp_pipe non-blocking */ + if (config->qos == QOS_NON_BLOCKING) { + if (set_flags(disp_pipe[0], O_NONBLOCK) < 0 || + set_flags(disp_pipe[1], O_NONBLOCK) < 0) { + audit_msg(LOG_ERR, "Failed to set O_NONBLOCK flag"); + return 1; + } + } + + // do the fork + pid = fork(); + switch(pid) { + case 0: // child + dup2(disp_pipe[0], 0); + close(disp_pipe[0]); + close(disp_pipe[1]); + sigfillset (&sa.sa_mask); + sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); + setsid(); + execl(config->dispatcher, config->dispatcher, NULL); + audit_msg(LOG_ERR, "exec() failed"); + exit(1); + break; + case -1: // error + return 1; + break; + default: // parent + close(disp_pipe[0]); + disp_pipe[0] = -1; + /* Avoid leaking this */ + if (fcntl(disp_pipe[1], F_SETFD, FD_CLOEXEC) < 0) { + audit_msg(LOG_ERR, + "Failed to set FD_CLOEXEC flag"); + return 1; + } + audit_msg(LOG_INFO, "Started dispatcher: %s pid: %u", + config->dispatcher, pid); + break; + } + + return 0; +} + +void shutdown_dispatcher(void) +{ + // kill child + if (pid) + kill(pid, SIGTERM); + // wait for term + // if not in time, send sigkill + pid = 0; + + // cleanup comm pipe + if (disp_pipe[0] >= 0) { + close(disp_pipe[0]); + disp_pipe[0] = -1; + } + if (disp_pipe[1] >= 0) { + close(disp_pipe[1]); + disp_pipe[1] = -1; + } +} + +void reconfigure_dispatcher(const struct daemon_conf *config) +{ + // signal child or start it so it can see if config changed + if (pid) + kill(pid, SIGHUP); + else + init_dispatcher(config); +} + +/* Returns -1 on err, 0 on success, and 1 if eagain occurred and not an err */ +int dispatch_event(const struct audit_reply *rep, int is_err) +{ + int rc, count = 0; + struct iovec vec[2]; + struct audit_dispatcher_header hdr; + + if (disp_pipe[1] == -1) + return 0; + + // Don't send reconfig or rotate as they are purely internal to daemon + if (rep->type == AUDIT_DAEMON_RECONFIG || + rep->type == AUDIT_DAEMON_ROTATE) + return 0; + + hdr.ver = AUDISP_PROTOCOL_VER; /* Hard-coded to current protocol */ + hdr.hlen = sizeof(struct audit_dispatcher_header); + hdr.type = rep->type; + hdr.size = rep->len; + + vec[0].iov_base = (void*)&hdr; + vec[0].iov_len = sizeof(hdr); + vec[1].iov_base = (void*)rep->message; + vec[1].iov_len = rep->len; + + do { + rc = writev(disp_pipe[1], vec, 2); + } while (rc < 0 && errno == EAGAIN && count++ < 8); + + // close pipe if no child or peer has been lost + if (rc <= 0) { + if (errno == EPIPE) { + shutdown_dispatcher(); + n_errs = 0; + } else if (errno == EAGAIN && !is_err) { + return 1; + } else { + if (n_errs <= REPORT_LIMIT) { + audit_msg(LOG_ERR, + "dispatch err (%s) event lost", + errno == EAGAIN ? "pipe full" : + strerror(errno)); + n_errs++; + } + if (n_errs == REPORT_LIMIT) { + audit_msg(LOG_ERR, + "dispatch error reporting limit" + " reached - ending report" + " notification."); + n_errs++; + } + return -1; + } + } else + n_errs = 0; + return 0; +} + diff --git a/framework/src/audit/src/auditd-dispatch.h b/framework/src/audit/src/auditd-dispatch.h new file mode 100644 index 00000000..25d32352 --- /dev/null +++ b/framework/src/audit/src/auditd-dispatch.h @@ -0,0 +1,37 @@ +/* auditd-dispatch.h -- + * Copyright 2005,2007,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUDITD_DISPATCH_H +#define AUDITD_DISPATCH_H + +#include "auditd-config.h" + +int dispatcher_pid(void); +void dispatcher_reaped(void); +int init_dispatcher(const struct daemon_conf *config); +void shutdown_dispatcher(void); +void reconfigure_dispatcher(const struct daemon_conf *config); +int dispatch_event(const struct audit_reply *rep, int is_err); + +#endif + diff --git a/framework/src/audit/src/auditd-event.c b/framework/src/audit/src/auditd-event.c new file mode 100644 index 00000000..ed0272e2 --- /dev/null +++ b/framework/src/audit/src/auditd-event.c @@ -0,0 +1,1407 @@ +/* auditd-event.c -- + * Copyright 2004-08,2011,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include /* O_NOFOLLOW needs gnu defined */ +#include +#include +#include +#include +#include +#include /* POSIX_HOST_NAME_MAX */ +#include "auditd-event.h" +#include "auditd-dispatch.h" +#include "auditd-listen.h" +#include "libaudit.h" +#include "private.h" + +/* This is defined in auditd.c */ +extern volatile int stop; + +struct auditd_consumer_data { + struct daemon_conf *config; + pthread_mutex_t queue_lock; + pthread_cond_t queue_nonempty; + struct auditd_reply_list *head; + struct auditd_reply_list *tail; + int log_fd; + FILE *log_file; +}; + +/* Local function prototypes */ +static void *event_thread_main(void *arg); +static void handle_event(struct auditd_consumer_data *data); +static void write_to_log(const char *buf, struct auditd_consumer_data *data); +static void check_log_file_size(struct auditd_consumer_data *data); +static void check_space_left(int lfd, struct auditd_consumer_data *data); +static void do_space_left_action(struct auditd_consumer_data *data, int admin); +static void do_disk_full_action(struct auditd_consumer_data *data); +static void do_disk_error_action(const char *func, struct daemon_conf *config, + int err); +static void check_excess_logs(struct auditd_consumer_data *data); +static void rotate_logs_now(struct auditd_consumer_data *data); +static void rotate_logs(struct auditd_consumer_data *data, + unsigned int num_logs); +static void shift_logs(struct auditd_consumer_data *data); +static int open_audit_log(struct auditd_consumer_data *data); +static void change_runlevel(const char *level); +static void safe_exec(const char *exe); +static char *format_raw(const struct audit_reply *rep, + struct daemon_conf *config); +static void reconfigure(struct auditd_consumer_data *data); + + +/* Local Data */ +static struct auditd_consumer_data consumer_data; +static pthread_t event_thread; +static unsigned int disk_err_warning = 0; +static int fs_space_warning = 0; +static int fs_admin_space_warning = 0; +static int fs_space_left = 1; +static int logging_suspended = 0; +static const char *SINGLE = "1"; +static const char *HALT = "0"; +static char *format_buf = NULL; +static off_t log_size = 0; + + +void shutdown_events(void) +{ + /* Give it 5 seconds to clear the queue */ + alarm(5); + pthread_join(event_thread, NULL); + free((void *)format_buf); + fclose(consumer_data.log_file); +} + +int init_event(struct daemon_conf *config) +{ + /* Store the netlink descriptor and config info away */ + consumer_data.config = config; + consumer_data.log_fd = -1; + + /* Setup IPC mechanisms */ + pthread_mutex_init(&consumer_data.queue_lock, NULL); + pthread_cond_init(&consumer_data.queue_nonempty, NULL); + + /* Reset the queue */ + consumer_data.head = consumer_data.tail = NULL; + + /* Now open the log */ + if (config->daemonize == D_BACKGROUND) { + if (open_audit_log(&consumer_data)) + return 1; + } else { + consumer_data.log_fd = 1; // stdout + consumer_data.log_file = fdopen(consumer_data.log_fd, "a"); + if (consumer_data.log_file == NULL) { + audit_msg(LOG_ERR, + "Error setting up stdout descriptor (%s)", + strerror(errno)); + return 1; + } + /* Set it to line buffering */ + setlinebuf(consumer_data.log_file); + } + + /* Create the worker thread */ + if (pthread_create(&event_thread, NULL, + event_thread_main, &consumer_data) < 0) { + audit_msg(LOG_ERR, "Couldn't create event thread, exiting"); + fclose(consumer_data.log_file); + return 1; + } + + if (config->daemonize == D_BACKGROUND) { + check_log_file_size(&consumer_data); + check_excess_logs(&consumer_data); + check_space_left(consumer_data.log_fd, &consumer_data); + } + format_buf = (char *)malloc(MAX_AUDIT_MESSAGE_LENGTH + + _POSIX_HOST_NAME_MAX); + if (format_buf == NULL) { + audit_msg(LOG_ERR, "No memory for formatting, exiting"); + fclose(consumer_data.log_file); + return 1; + } + return 0; +} + +/* This function takes a malloc'd rep and places it on the queue. The + dequeue'r is responsible for freeing the memory. */ +void enqueue_event(struct auditd_reply_list *rep) +{ + char *buf = NULL; + int len; + + rep->ack_func = 0; + rep->ack_data = 0; + rep->sequence_id = 0; + + if (rep->reply.type != AUDIT_DAEMON_RECONFIG) { + switch (consumer_data.config->log_format) + { + case LF_RAW: + buf = format_raw(&rep->reply, consumer_data.config); + break; + case LF_NOLOG: + // We need the rotate event to get enqueued + if (rep->reply.type != AUDIT_DAEMON_ROTATE ) { + // Internal DAEMON messages should be free'd + if (rep->reply.type >= AUDIT_FIRST_DAEMON && + rep->reply.type <= AUDIT_LAST_DAEMON) + free((void *)rep->reply.message); + free(rep); + return; + } + break; + default: + audit_msg(LOG_ERR, + "Illegal log format detected %d", + consumer_data.config->log_format); + // Internal DAEMON messages should be free'd + if (rep->reply.type >= AUDIT_FIRST_DAEMON && + rep->reply.type <= AUDIT_LAST_DAEMON) + free((void *)rep->reply.message); + free(rep); + return; + } + + if (buf) { + len = strlen(buf); + if (len < MAX_AUDIT_MESSAGE_LENGTH - 1) + memcpy(rep->reply.msg.data, buf, len+1); + else { + // FIXME: is truncation the right thing to do? + memcpy(rep->reply.msg.data, buf, + MAX_AUDIT_MESSAGE_LENGTH-1); + rep->reply.msg.data[MAX_AUDIT_MESSAGE_LENGTH-1] = 0; + } + } + } + + rep->next = NULL; /* new packet goes at end - so zero this */ + + pthread_mutex_lock(&consumer_data.queue_lock); + if (consumer_data.head == NULL) { + consumer_data.head = consumer_data.tail = rep; + pthread_cond_signal(&consumer_data.queue_nonempty); + } else { + /* FIXME: wait for room on the queue */ + + /* OK there's room...add it in */ + consumer_data.tail->next = rep; /* link in at end */ + consumer_data.tail = rep; /* move end to newest */ + } + pthread_mutex_unlock(&consumer_data.queue_lock); +} + +/* This function takes a preformatted message and places it on the + queue. The dequeue'r is responsible for freeing the memory. */ +void enqueue_formatted_event(char *msg, ack_func_type ack_func, void *ack_data, uint32_t sequence_id) +{ + int len; + struct auditd_reply_list *rep; + + rep = (struct auditd_reply_list *) calloc (1, sizeof (*rep)); + if (rep == NULL) { + audit_msg(LOG_ERR, "Cannot allocate audit reply"); + return; + } + + rep->ack_func = ack_func; + rep->ack_data = ack_data; + rep->sequence_id = sequence_id; + + len = strlen (msg); + if (len < MAX_AUDIT_MESSAGE_LENGTH - 1) + memcpy (rep->reply.msg.data, msg, len+1); + else { + /* FIXME: is truncation the right thing to do? */ + memcpy (rep->reply.msg.data, msg, MAX_AUDIT_MESSAGE_LENGTH-1); + rep->reply.msg.data[MAX_AUDIT_MESSAGE_LENGTH-1] = 0; + } + + pthread_mutex_lock(&consumer_data.queue_lock); + if (consumer_data.head == NULL) { + consumer_data.head = consumer_data.tail = rep; + pthread_cond_signal(&consumer_data.queue_nonempty); + } else { + /* FIXME: wait for room on the queue */ + + /* OK there's room...add it in */ + consumer_data.tail->next = rep; /* link in at end */ + consumer_data.tail = rep; /* move end to newest */ + } + pthread_mutex_unlock(&consumer_data.queue_lock); +} + +void resume_logging(void) +{ + logging_suspended = 0; + fs_space_left = 1; + disk_err_warning = 0; + fs_space_warning = 0; + fs_admin_space_warning = 0; + audit_msg(LOG_ERR, "Audit daemon is attempting to resume logging."); +} + +static void *event_thread_main(void *arg) +{ + struct auditd_consumer_data *data = arg; + sigset_t sigs; + + /* This is a worker thread. Don't handle signals. */ + sigemptyset(&sigs); + sigaddset(&sigs, SIGALRM); + sigaddset(&sigs, SIGTERM); + sigaddset(&sigs, SIGHUP); + sigaddset(&sigs, SIGUSR1); + sigaddset(&sigs, SIGUSR2); + pthread_sigmask(SIG_SETMASK, &sigs, NULL); + + while (1) { + struct auditd_reply_list *cur; + int stop_req = 0; +// FIXME: wait for data + pthread_mutex_lock(&data->queue_lock); + while (data->head == NULL) { + pthread_cond_wait(&data->queue_nonempty, + &data->queue_lock); + } +// FIXME: at this point we can use data->head unlocked since it won't change. + handle_event(data); + cur = data->head; +// FIXME: relock at this point + if (data->tail == data->head) + data->tail = NULL; + data->head = data->head->next; + if (data->head == NULL && stop && + ( cur->reply.type == AUDIT_DAEMON_END || + cur->reply.type == AUDIT_DAEMON_ABORT) ) + stop_req = 1; + pthread_mutex_unlock(&data->queue_lock); + + /* Internal DAEMON messages should be free'd */ + if (cur->reply.type >= AUDIT_FIRST_DAEMON && + cur->reply.type <= AUDIT_LAST_DAEMON) { + free((void *)cur->reply.message); + } + free(cur); + if (stop_req) + break; + } + return NULL; +} + + +/* This function takes the newly dequeued event and handles it. */ +static unsigned int count = 0L; +static void handle_event(struct auditd_consumer_data *data) +{ + char *buf = data->head->reply.msg.data; + + if (data->head->reply.type == AUDIT_DAEMON_RECONFIG) { + reconfigure(data); + switch (consumer_data.config->log_format) + { + case LF_RAW: + buf = format_raw(&data->head->reply, consumer_data.config); + break; + case LF_NOLOG: + return; + default: + audit_msg(LOG_ERR, + "Illegal log format detected %d", + consumer_data.config->log_format); + return; + } + } else if (data->head->reply.type == AUDIT_DAEMON_ROTATE) { + rotate_logs_now(data); + if (consumer_data.config->log_format == LF_NOLOG) + return; + } + if (!logging_suspended) { + + write_to_log(buf, data); + + /* See if we need to flush to disk manually */ + if (data->config->flush == FT_INCREMENTAL) { + count++; + if ((count % data->config->freq) == 0) { + int rc; + errno = 0; + do { + rc = fflush(data->log_file); + } while (rc < 0 && errno == EINTR); + if (errno) { + if (errno == ENOSPC && + fs_space_left == 1) { + fs_space_left = 0; + do_disk_full_action(data); + } else + //EIO is only likely failure mode + do_disk_error_action("flush", + data->config, errno); + } + + /* EIO is only likely failure mode */ + if ((data->config->daemonize == D_BACKGROUND)&& + (fsync(data->log_fd) != 0)) { + do_disk_error_action("fsync", + data->config, errno); + } + } + } + } +} + +static void send_ack(struct auditd_consumer_data *data, int ack_type, + const char *msg) +{ + if (data->head->ack_func) { + unsigned char header[AUDIT_RMW_HEADER_SIZE]; + + AUDIT_RMW_PACK_HEADER(header, 0, ack_type, strlen(msg), + data->head->sequence_id); + + data->head->ack_func(data->head->ack_data, header, msg); + } +} + +/* This function writes the given buf to the current log file */ +static void write_to_log(const char *buf, struct auditd_consumer_data *data) +{ + int rc; + FILE *f = data->log_file; + struct daemon_conf *config = data->config; + int ack_type = AUDIT_RMW_TYPE_ACK; + const char *msg = ""; + + /* write it to disk */ + rc = fprintf(f, "%s\n", buf); + + /* error? Handle it */ + if (rc < 0) { + if (errno == ENOSPC) { + ack_type = AUDIT_RMW_TYPE_DISKFULL; + msg = "disk full"; + send_ack(data, ack_type, msg); + if (fs_space_left == 1) { + fs_space_left = 0; + do_disk_full_action(data); + } + } else { + int saved_errno = errno; + ack_type = AUDIT_RMW_TYPE_DISKERROR; + msg = "disk write error"; + send_ack(data, ack_type, msg); + do_disk_error_action("write", config, saved_errno); + } + } else { + /* check log file size & space left on partition */ + if (config->daemonize == D_BACKGROUND) { + // If either of these fail, I consider it an + // inconvenience as opposed to something that is + // actionable. There may be some temporary condition + // that the system recovers from. The real error + // occurs on write. + log_size += rc; + check_log_file_size(data); + check_space_left(data->log_fd, data); + } + + if (fs_space_warning) + ack_type = AUDIT_RMW_TYPE_DISKLOW; + send_ack(data, ack_type, msg); + disk_err_warning = 0; + } +} + +static void check_log_file_size(struct auditd_consumer_data *data) +{ + struct daemon_conf *config = data->config; + + /* did we cross the size limit? */ + off_t sz = log_size / MEGABYTE; + + if (sz >= config->max_log_size && (config->daemonize == D_BACKGROUND)) { + switch (config->max_log_size_action) + { + case SZ_IGNORE: + break; + case SZ_SYSLOG: + audit_msg(LOG_ERR, + "Audit daemon log file is larger than max size"); + break; + case SZ_SUSPEND: + audit_msg(LOG_ERR, + "Audit daemon is suspending logging due to logfile size."); + logging_suspended = 1; + break; + case SZ_ROTATE: + if (data->config->num_logs > 1) { + audit_msg(LOG_NOTICE, + "Audit daemon rotating log files"); + rotate_logs(data, 0); + } + break; + case SZ_KEEP_LOGS: + audit_msg(LOG_NOTICE, + "Audit daemon rotating log files with keep option"); + shift_logs(data); + break; + default: + audit_msg(LOG_ALERT, + "Audit daemon log file is larger than max size and unknown action requested"); + break; + } + } +} + +static void check_space_left(int lfd, struct auditd_consumer_data *data) +{ + int rc; + struct statfs buf; + struct daemon_conf *config = data->config; + + rc = fstatfs(lfd, &buf); + if (rc == 0) { + if (buf.f_bavail < 5) { + /* we won't consume the last 5 blocks */ + fs_space_left = 0; + do_disk_full_action(data); + } else { + unsigned long blocks; + unsigned long block_size = buf.f_bsize; + blocks = config->space_left * (MEGABYTE/block_size); + if (buf.f_bavail < blocks) { + if (fs_space_warning == 0) { + do_space_left_action(data, 0); + fs_space_warning = 1; + } + } else if (fs_space_warning && + config->space_left_action == FA_SYSLOG){ + // Auto reset only if failure action is syslog + fs_space_warning = 0; + } + blocks=config->admin_space_left * (MEGABYTE/block_size); + if (buf.f_bavail < blocks) { + if (fs_admin_space_warning == 0) { + do_space_left_action(data, 1); + fs_admin_space_warning = 1; + } + } else if (fs_admin_space_warning && + config->admin_space_left_action == FA_SYSLOG) { + // Auto reset only if failure action is syslog + fs_admin_space_warning = 0; + } + } + } + else audit_msg(LOG_DEBUG, "fstatfs returned:%d, %s", rc, + strerror(errno)); +} + +extern int sendmail(const char *subject, const char *content, + const char *mail_acct); +static void do_space_left_action(struct auditd_consumer_data *data, int admin) +{ + int action; + struct daemon_conf *config = data->config; + + if (admin) + action = config->admin_space_left_action; + else + action = config->space_left_action; + + switch (action) + { + case FA_IGNORE: + break; + case FA_SYSLOG: + audit_msg(LOG_ALERT, + "Audit daemon is low on disk space for logging"); + break; + case FA_ROTATE: + if (config->num_logs > 1) { + audit_msg(LOG_NOTICE, + "Audit daemon rotating log files"); + rotate_logs(data, 0); + } + break; + case FA_EMAIL: + if (admin == 0) { + sendmail("Audit Disk Space Alert", + "The audit daemon is low on disk space for logging! Please take action\nto ensure no loss of service.", + config->action_mail_acct); + audit_msg(LOG_ALERT, + "Audit daemon is low on disk space for logging"); + } else { + sendmail("Audit Admin Space Alert", + "The audit daemon is very low on disk space for logging! Immediate action\nis required to ensure no loss of service.", + config->action_mail_acct); + audit_msg(LOG_ALERT, + "Audit daemon is very low on disk space for logging"); + } + break; + case FA_EXEC: + if (admin) + safe_exec(config->admin_space_left_exe); + else + safe_exec(config->space_left_exe); + break; + case FA_SUSPEND: + audit_msg(LOG_ALERT, + "Audit daemon is suspending logging due to low disk space."); + logging_suspended = 1; + break; + case FA_SINGLE: + audit_msg(LOG_ALERT, + "The audit daemon is now changing the system to single user mode"); + change_runlevel(SINGLE); + break; + case FA_HALT: + audit_msg(LOG_ALERT, + "The audit daemon is now halting the system"); + change_runlevel(HALT); + break; + default: + audit_msg(LOG_ALERT, + "Audit daemon is low on disk space for logging and unknown action requested"); + break; + } +} + +static void do_disk_full_action(struct auditd_consumer_data *data) +{ + struct daemon_conf *config = data->config; + + audit_msg(LOG_ALERT, + "Audit daemon has no space left on logging partition"); + switch (config->disk_full_action) + { + case FA_IGNORE: + case FA_SYSLOG: /* Message is syslogged above */ + break; + case FA_ROTATE: + if (config->num_logs > 1) { + audit_msg(LOG_NOTICE, + "Audit daemon rotating log files"); + rotate_logs(data, 0); + } + break; + case FA_EXEC: + safe_exec(config->disk_full_exe); + break; + case FA_SUSPEND: + audit_msg(LOG_ALERT, + "Audit daemon is suspending logging due to no space left on logging partition."); + logging_suspended = 1; + break; + case FA_SINGLE: + audit_msg(LOG_ALERT, + "The audit daemon is now changing the system to single user mode due to no space left on logging partition"); + change_runlevel(SINGLE); + break; + case FA_HALT: + audit_msg(LOG_ALERT, + "The audit daemon is now halting the system due to no space left on logging partition"); + change_runlevel(HALT); + break; + default: + audit_msg(LOG_ALERT, "Unknown disk full action requested"); + break; + } +} + +static void do_disk_error_action(const char * func, struct daemon_conf *config, + int err) +{ + char text[128]; + + switch (config->disk_error_action) + { + case FA_IGNORE: + break; + case FA_SYSLOG: + if (disk_err_warning < 5) { + snprintf(text, sizeof(text), + "%s: Audit daemon detected an error writing an event to disk (%s)", + func, strerror(err)); + audit_msg(LOG_ALERT, "%s", text); + disk_err_warning++; + } + break; + case FA_EXEC: + safe_exec(config->disk_error_exe); + break; + case FA_SUSPEND: + audit_msg(LOG_ALERT, + "Audit daemon is suspending logging due to previously mentioned write error"); + logging_suspended = 1; + break; + case FA_SINGLE: + audit_msg(LOG_ALERT, + "The audit daemon is now changing the system to single user mode due to previously mentioned write error"); + change_runlevel(SINGLE); + break; + case FA_HALT: + audit_msg(LOG_ALERT, + "The audit daemon is now halting the system due to previously mentioned write error."); + change_runlevel(HALT); + break; + default: + audit_msg(LOG_ALERT, + "Unknown disk error action requested"); + break; + } +} + +static void rotate_logs_now(struct auditd_consumer_data *data) +{ + struct daemon_conf *config = data->config; + + if (config->max_log_size_action == SZ_KEEP_LOGS) + shift_logs(data); + else + rotate_logs(data, 0); +} + +/* Check for and remove excess logs so that we don't run out of room */ +static void check_excess_logs(struct auditd_consumer_data *data) +{ + int rc; + unsigned int i, len; + char *name; + + // Only do this if rotate is the log size action + // and we actually have a limit + if (data->config->max_log_size_action != SZ_ROTATE || + data->config->num_logs < 2) + return; + + len = strlen(data->config->log_file) + 16; + name = (char *)malloc(len); + if (name == NULL) { /* Not fatal - just messy */ + audit_msg(LOG_ERR, "No memory checking excess logs"); + return; + } + + // We want 1 beyond the normal logs + i=data->config->num_logs; + rc=0; + while (rc == 0) { + snprintf(name, len, "%s.%d", data->config->log_file, i++); + rc=unlink(name); + if (rc == 0) + audit_msg(LOG_NOTICE, + "Log %s removed as it exceeds num_logs parameter", + name); + } + free(name); +} + +static void rotate_logs(struct auditd_consumer_data *data, + unsigned int num_logs) +{ + int rc; + unsigned int len, i; + char *oldname, *newname; + + if (data->config->max_log_size_action == SZ_ROTATE && + data->config->num_logs < 2) + return; + + /* Close audit file. fchmod and fchown errors are not fatal because we + * already adjusted log file permissions and ownership when opening the + * log file. */ + if (fchmod(data->log_fd, data->config->log_group ? S_IRUSR|S_IRGRP : + S_IRUSR) < 0) { + audit_msg(LOG_NOTICE, "Couldn't change permissions while " + "rotating log file (%s)", strerror(errno)); + } + if (fchown(data->log_fd, 0, data->config->log_group) < 0) { + audit_msg(LOG_NOTICE, "Couldn't change ownership while " + "rotating log file (%s)", strerror(errno)); + } + fclose(data->log_file); + + /* Rotate */ + len = strlen(data->config->log_file) + 16; + oldname = (char *)malloc(len); + if (oldname == NULL) { /* Not fatal - just messy */ + audit_msg(LOG_ERR, "No memory rotating logs"); + logging_suspended = 1; + return; + } + newname = (char *)malloc(len); + if (newname == NULL) { /* Not fatal - just messy */ + audit_msg(LOG_ERR, "No memory rotating logs"); + free(oldname); + logging_suspended = 1; + return; + } + + /* If we are rotating, get number from config */ + if (num_logs == 0) + num_logs = data->config->num_logs; + + /* Handle this case first since it will not enter the for loop */ + if (num_logs == 2) + snprintf(oldname, len, "%s.1", data->config->log_file); + + for (i=num_logs - 1; i>1; i--) { + snprintf(oldname, len, "%s.%d", data->config->log_file, i-1); + snprintf(newname, len, "%s.%d", data->config->log_file, i); + /* if the old file exists */ + rc = rename(oldname, newname); + if (rc == -1 && errno != ENOENT) { + // Likely errors: ENOSPC, ENOMEM, EBUSY + int saved_errno = errno; + audit_msg(LOG_ERR, + "Error rotating logs from %s to %s (%s)", + oldname, newname, strerror(errno)); + if (saved_errno == ENOSPC && fs_space_left == 1) { + fs_space_left = 0; + do_disk_full_action(data); + } else + do_disk_error_action("rotate", data->config, + saved_errno); + } + } + free(newname); + + /* At this point, oldname should point to lowest number - use it */ + newname = oldname; + rc = rename(data->config->log_file, newname); + if (rc == -1 && errno != ENOENT) { + // Likely errors: ENOSPC, ENOMEM, EBUSY + int saved_errno = errno; + audit_msg(LOG_ERR, "Error rotating logs from %s to %s (%s)", + data->config->log_file, newname, strerror(errno)); + if (saved_errno == ENOSPC && fs_space_left == 1) { + fs_space_left = 0; + do_disk_full_action(data); + } else + do_disk_error_action("rotate2", data->config, + saved_errno); + + /* At this point, we've failed to rotate the original log. + * So, let's make the old log writable and try again next + * time */ + chmod(data->config->log_file, + data->config->log_group ? S_IWUSR|S_IRUSR|S_IRGRP : + S_IWUSR|S_IRUSR); + } + free(newname); + + /* open new audit file */ + if (open_audit_log(data)) { + int saved_errno = errno; + audit_msg(LOG_NOTICE, + "Could not reopen a log after rotating."); + logging_suspended = 1; + do_disk_error_action("reopen", data->config, saved_errno); + } +} + +static int last_log = 1; +static void shift_logs(struct auditd_consumer_data *data) +{ + // The way this has to work is to start scanning from .1 up until + // no file is found. Then do the rotate algorithm using that number + // instead of log_max. + unsigned int num_logs, len; + char *name; + + len = strlen(data->config->log_file) + 16; + name = (char *)malloc(len); + if (name == NULL) { /* Not fatal - just messy */ + audit_msg(LOG_ERR, "No memory shifting logs"); + return; + } + + // Find last log + num_logs = last_log; + while (num_logs) { + snprintf(name, len, "%s.%d", data->config->log_file, + num_logs); + if (access(name, R_OK) != 0) + break; + num_logs++; + } + + /* Our last known file disappeared, start over... */ + if (num_logs <= last_log && last_log > 1) { + audit_msg(LOG_WARNING, "Last known log disappeared (%s)", name); + num_logs = last_log = 1; + while (num_logs) { + snprintf(name, len, "%s.%d", data->config->log_file, + num_logs); + if (access(name, R_OK) != 0) + break; + num_logs++; + } + audit_msg(LOG_INFO, "Next log to use will be %s", name); + } + last_log = num_logs; + rotate_logs(data, num_logs+1); + free(name); +} + +/* + * This function handles opening a descriptor for the audit log + * file and ensuring the correct options are applied to the descriptor. + * It returns 0 on success and 1 on failure. + */ +static int open_audit_log(struct auditd_consumer_data *data) +{ + int flags, lfd; + + // Likely errors for open: Almost anything + // Likely errors on rotate: ENFILE, ENOMEM, ENOSPC +retry: + lfd = open(data->config->log_file, O_WRONLY|O_APPEND|O_NOFOLLOW); + if (lfd < 0) { + if (errno == ENOENT) { + lfd = create_log_file(data->config->log_file); + if (lfd < 0) { + audit_msg(LOG_ERR, + "Couldn't create log file %s (%s)", + data->config->log_file, + strerror(errno)); + return 1; + } + close(lfd); + lfd = open(data->config->log_file, + O_WRONLY|O_APPEND|O_NOFOLLOW); + log_size = 0; + } else if (errno == ENFILE) { + // All system descriptors used, try again... + goto retry; + } + if (lfd < 0) { + audit_msg(LOG_ERR, "Couldn't open log file %s (%s)", + data->config->log_file, strerror(errno)); + return 1; + } + } else { + // Get initial size + struct stat st; + + int rc = fstat(lfd, &st); + if (rc == 0) + log_size = st.st_size; + else { + close(lfd); + return 1; + } + } + + if (fcntl(lfd, F_SETFD, FD_CLOEXEC) == -1) { + close(lfd); + audit_msg(LOG_ERR, "Error setting log file CLOEXEC flag (%s)", + strerror(errno)); + return 1; + } + if (data->config->flush == FT_DATA) { + flags = fcntl(lfd, F_GETFL); + if (flags < 0) { + audit_msg(LOG_ERR, "Couldn't get log file flags (%s)", + strerror(errno)); + close(lfd); + return 1; + } + if (fcntl(lfd, F_SETFL, flags|O_DSYNC) < 0) { + audit_msg(LOG_ERR, + "Couldn't set data sync mode on log file (%s)", + strerror(errno)); + close(lfd); + return 1; + } + } + else if (data->config->flush == FT_SYNC){ + flags = fcntl(lfd, F_GETFL); + if (flags < 0) { + audit_msg(LOG_ERR, "Couldn't get log file flags (%s)", + strerror(errno)); + close(lfd); + return 1; + } + if (fcntl(lfd, F_SETFL, flags|O_SYNC) < 0) { + audit_msg(LOG_ERR, + "Couldn't set sync mode on log file (%s)", + strerror(errno)); + close(lfd); + return 1; + } + } + if (fchmod(lfd, data->config->log_group ? S_IRUSR|S_IWUSR|S_IRGRP : + S_IRUSR|S_IWUSR) < 0) { + audit_msg(LOG_ERR, + "Couldn't change permissions of log file (%s)", + strerror(errno)); + close(lfd); + return 1; + } + if (fchown(lfd, 0, data->config->log_group) < 0) { + audit_msg(LOG_ERR, "Couldn't change ownership of log file (%s)", + strerror(errno)); + close(lfd); + return 1; + } + + data->log_fd = lfd; + data->log_file = fdopen(lfd, "a"); + if (data->log_file == NULL) { + audit_msg(LOG_ERR, "Error setting up log descriptor (%s)", + strerror(errno)); + close(lfd); + return 1; + } + + /* Set it to line buffering */ + setlinebuf(consumer_data.log_file); + return 0; +} + +static void change_runlevel(const char *level) +{ + char *argv[3]; + int pid; + struct sigaction sa; + static const char *init_pgm = "/sbin/init"; + + pid = fork(); + if (pid < 0) { + audit_msg(LOG_ALERT, + "Audit daemon failed to fork switching runlevels"); + return; + } + if (pid) /* Parent */ + return; + /* Child */ + sigfillset (&sa.sa_mask); + sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); + + argv[0] = (char *)init_pgm; + argv[1] = (char *)level; + argv[2] = NULL; + execve(init_pgm, argv, NULL); + audit_msg(LOG_ALERT, "Audit daemon failed to exec %s", init_pgm); + exit(1); +} + +static void safe_exec(const char *exe) +{ + char *argv[2]; + int pid; + struct sigaction sa; + + if (exe == NULL) { + audit_msg(LOG_ALERT, + "Safe_exec passed NULL for program to execute"); + return; + } + + pid = fork(); + if (pid < 0) { + audit_msg(LOG_ALERT, + "Audit daemon failed to fork doing safe_exec"); + return; + } + if (pid) /* Parent */ + return; + /* Child */ + sigfillset (&sa.sa_mask); + sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); + + argv[0] = (char *)exe; + argv[1] = NULL; + execve(exe, argv, NULL); + audit_msg(LOG_ALERT, "Audit daemon failed to exec %s", exe); + exit(1); +} + +/* +* This function will take an audit structure and return a +* text buffer that's unformatted for writing to disk. If there +* is an error the return value is NULL. +*/ +static char *format_raw(const struct audit_reply *rep, + struct daemon_conf *config) +{ + char *ptr; + + if (rep==NULL) { + if (config->node_name_format != N_NONE) + snprintf(format_buf, MAX_AUDIT_MESSAGE_LENGTH + + _POSIX_HOST_NAME_MAX - 32, + "node=%s type=DAEMON msg=NULL reply", + config->node_name); + else + snprintf(format_buf, MAX_AUDIT_MESSAGE_LENGTH, + "type=DAEMON msg=NULL reply"); + } else { + int len, nlen; + const char *type, *message; + char unknown[32]; + type = audit_msg_type_to_name(rep->type); + if (type == NULL) { + snprintf(unknown, sizeof(unknown), + "UNKNOWN[%d]", rep->type); + type = unknown; + } + if (rep->message == NULL) { + message = "msg lost"; + len = 8; + } else { + message = rep->message; + len = rep->len; + } + + // Note: This can truncate messages if + // MAX_AUDIT_MESSAGE_LENGTH is too small + if (config->node_name_format != N_NONE) + nlen = snprintf(format_buf, MAX_AUDIT_MESSAGE_LENGTH + + _POSIX_HOST_NAME_MAX - 32, + "node=%s type=%s msg=%.*s\n", + config->node_name, type, len, message); + else + nlen = snprintf(format_buf, + MAX_AUDIT_MESSAGE_LENGTH - 32, + "type=%s msg=%.*s", type, len, message); + + /* Replace \n with space so it looks nicer. */ + ptr = format_buf; + while ((ptr = strchr(ptr, 0x0A)) != NULL) + *ptr = ' '; + + /* Trim trailing space off since it wastes space */ + if (format_buf[nlen-1] == ' ') + format_buf[nlen-1] = 0; + } + return format_buf; +} + +static void reconfigure(struct auditd_consumer_data *data) +{ + struct daemon_conf *nconf = data->head->reply.conf; + struct daemon_conf *oconf = data->config; + uid_t uid = nconf->sender_uid; + pid_t pid = nconf->sender_pid; + const char *ctx = nconf->sender_ctx; + struct timeval tv; + char txt[MAX_AUDIT_MESSAGE_LENGTH]; + char date[40]; + unsigned int seq_num; + int need_size_check = 0, need_reopen = 0, need_space_check = 0; + + snprintf(txt, sizeof(txt), + "config change requested by pid=%d auid=%u subj=%s", + pid, uid, ctx); + audit_msg(LOG_NOTICE, "%s", txt); + + /* Do the reconfiguring. These are done in a specific + * order from least invasive to most invasive. We will + * start with general system parameters. */ + + // start with disk error action. + oconf->disk_error_action = nconf->disk_error_action; + free((char *)oconf->disk_error_exe); + oconf->disk_error_exe = nconf->disk_error_exe; + disk_err_warning = 0; + + // numlogs is next + oconf->num_logs = nconf->num_logs; + + // flush freq + oconf->freq = nconf->freq; + + // priority boost + if (oconf->priority_boost != nconf->priority_boost) { + int rc; + + oconf->priority_boost = nconf->priority_boost; + errno = 0; + rc = nice(-oconf->priority_boost); + if (rc == -1 && errno) + audit_msg(LOG_NOTICE, "Cannot change priority in " + "reconfigure (%s)", strerror(errno)); + } + + // log format + oconf->log_format = nconf->log_format; + + // action_mail_acct + if (strcmp(oconf->action_mail_acct, nconf->action_mail_acct)) { + free((void *)oconf->action_mail_acct); + oconf->action_mail_acct = nconf->action_mail_acct; + } else + free((void *)nconf->action_mail_acct); + + // node_name + if (oconf->node_name_format != nconf->node_name_format || + (oconf->node_name && nconf->node_name && + strcmp(oconf->node_name, nconf->node_name) != 0)) { + oconf->node_name_format = nconf->node_name_format; + free((char *)oconf->node_name); + oconf->node_name = nconf->node_name; + } + + /* Now look at audit dispatcher changes */ + oconf->qos = nconf->qos; // dispatcher qos + + // do the dispatcher app change + if (oconf->dispatcher || nconf->dispatcher) { + // none before, start new one + if (oconf->dispatcher == NULL) { + oconf->dispatcher = strdup(nconf->dispatcher); + if (oconf->dispatcher == NULL) { + int saved_errno = errno; + audit_msg(LOG_NOTICE, + "Could not allocate dispatcher memory" + " in reconfigure"); + // Likely errors: ENOMEM + do_disk_error_action("reconfig", data->config, + saved_errno); + } + if(init_dispatcher(oconf)) {// dispatcher & qos is used + int saved_errno = errno; + audit_msg(LOG_NOTICE, + "Could not start dispatcher %s" + " in reconfigure", oconf->dispatcher); + // Likely errors: Socketpairs or exec perms + do_disk_error_action("reconfig", data->config, + saved_errno); + } + } + // have one, but none after this + else if (nconf->dispatcher == NULL) { + shutdown_dispatcher(); + free((char *)oconf->dispatcher); + oconf->dispatcher = NULL; + } + // they are different apps + else if (strcmp(oconf->dispatcher, nconf->dispatcher)) { + shutdown_dispatcher(); + free((char *)oconf->dispatcher); + oconf->dispatcher = strdup(nconf->dispatcher); + if (oconf->dispatcher == NULL) { + int saved_errno = errno; + audit_msg(LOG_NOTICE, + "Could not allocate dispatcher memory" + " in reconfigure"); + // Likely errors: ENOMEM + do_disk_error_action("reconfig", data->config, + saved_errno); + } + if(init_dispatcher(oconf)) {// dispatcher & qos is used + int saved_errno = errno; + audit_msg(LOG_NOTICE, + "Could not start dispatcher %s" + " in reconfigure", oconf->dispatcher); + // Likely errors: Socketpairs or exec perms + do_disk_error_action("reconfig", data->config, + saved_errno); + } + } + // they are the same app - just signal it + else { + reconfigure_dispatcher(oconf); + free((char *)nconf->dispatcher); + nconf->dispatcher = NULL; + } + } + + // network listener + auditd_tcp_listen_reconfigure(nconf, oconf); + + /* At this point we will work on the items that are related to + * a single log file. */ + + // max logfile action + if (oconf->max_log_size_action != nconf->max_log_size_action) { + oconf->max_log_size_action = nconf->max_log_size_action; + need_size_check = 1; + } + + // max log size + if (oconf->max_log_size != nconf->max_log_size) { + oconf->max_log_size = nconf->max_log_size; + need_size_check = 1; + } + + if (need_size_check) { + logging_suspended = 0; + check_log_file_size(data); + } + + // flush technique + if (oconf->flush != nconf->flush) { + oconf->flush = nconf->flush; + need_reopen = 1; + } + + // logfile + if (strcmp(oconf->log_file, nconf->log_file)) { + free((void *)oconf->log_file); + oconf->log_file = nconf->log_file; + need_reopen = 1; + need_space_check = 1; // might be on new partition + } else + free((void *)nconf->log_file); + + if (need_reopen) { + fclose(data->log_file); + if (open_audit_log(data)) { + int saved_errno = errno; + audit_msg(LOG_NOTICE, + "Could not reopen a log after reconfigure"); + logging_suspended = 1; + // Likely errors: ENOMEM, ENOSPC + do_disk_error_action("reconfig", data->config, + saved_errno); + } else { + logging_suspended = 0; + check_log_file_size(data); + } + } + + /* At this point we will start working on items that are + * related to the amount of space on the partition. */ + + // space left + if (oconf->space_left != nconf->space_left) { + oconf->space_left = nconf->space_left; + need_space_check = 1; + } + + // space left action + if (oconf->space_left_action != nconf->space_left_action) { + oconf->space_left_action = nconf->space_left_action; + need_space_check = 1; + } + + // space left exe + if (oconf->space_left_exe || nconf->space_left_exe) { + if (nconf->space_left_exe == NULL) + ; /* do nothing if new one is blank */ + else if (oconf->space_left_exe == NULL && nconf->space_left_exe) + need_space_check = 1; + else if (strcmp(oconf->space_left_exe, nconf->space_left_exe)) + need_space_check = 1; + free((char *)oconf->space_left_exe); + oconf->space_left_exe = nconf->space_left_exe; + } + + // admin space left + if (oconf->admin_space_left != nconf->admin_space_left) { + oconf->admin_space_left = nconf->admin_space_left; + need_space_check = 1; + } + + // admin space action + if (oconf->admin_space_left_action != nconf->admin_space_left_action) { + oconf->admin_space_left_action = nconf->admin_space_left_action; + need_space_check = 1; + } + + // admin space left exe + if (oconf->admin_space_left_exe || nconf->admin_space_left_exe) { + if (nconf->admin_space_left_exe == NULL) + ; /* do nothing if new one is blank */ + else if (oconf->admin_space_left_exe == NULL && + nconf->admin_space_left_exe) + need_space_check = 1; + else if (strcmp(oconf->admin_space_left_exe, + nconf->admin_space_left_exe)) + need_space_check = 1; + free((char *)oconf->admin_space_left_exe); + oconf->admin_space_left_exe = nconf->admin_space_left_exe; + } + // disk full action + if (oconf->disk_full_action != nconf->disk_full_action) { + oconf->disk_full_action = nconf->disk_full_action; + need_space_check = 1; + } + + // disk full exe + if (oconf->disk_full_exe || nconf->disk_full_exe) { + if (nconf->disk_full_exe == NULL) + ; /* do nothing if new one is blank */ + else if (oconf->disk_full_exe == NULL && nconf->disk_full_exe) + need_space_check = 1; + else if (strcmp(oconf->disk_full_exe, nconf->disk_full_exe)) + need_space_check = 1; + free((char *)oconf->disk_full_exe); + oconf->disk_full_exe = nconf->disk_full_exe; + } + + if (need_space_check) { + /* note save suspended flag, then do space_left. If suspended + * is still 0, then copy saved suspended back. This avoids + * having to call check_log_file_size to restore it. */ + int saved_suspend = logging_suspended; + + fs_space_warning = 0; + fs_admin_space_warning = 0; + fs_space_left = 1; + logging_suspended = 0; + check_excess_logs(data); + check_space_left(data->log_fd, data); + if (logging_suspended == 0) + logging_suspended = saved_suspend; + } + + // Next document the results + srand(time(NULL)); + seq_num = rand()%10000; + if (gettimeofday(&tv, NULL) == 0) { + snprintf(date, sizeof(date), "audit(%lu.%03u:%u)", tv.tv_sec, + (unsigned)(tv.tv_usec/1000), seq_num); + } else { + snprintf(date, sizeof(date), + "audit(%lu.%03u:%u)", (unsigned long)time(NULL), + 0, seq_num); + } + + data->head->reply.len = snprintf(txt, sizeof(txt), + "%s config changed, auid=%u pid=%d subj=%s res=success", date, + uid, pid, ctx ); + audit_msg(LOG_NOTICE, "%s", txt); + data->head->reply.type = AUDIT_DAEMON_CONFIG; + data->head->reply.message = strdup(txt); + if (!data->head->reply.message) { + data->head->reply.len = 0; + audit_msg(LOG_ERR, "Cannot allocate config message"); + // FIXME: Should call some error handler + } + free((char *)ctx); +} + diff --git a/framework/src/audit/src/auditd-event.h b/framework/src/audit/src/auditd-event.h new file mode 100644 index 00000000..71678259 --- /dev/null +++ b/framework/src/audit/src/auditd-event.h @@ -0,0 +1,49 @@ +/* auditd-event.h -- + * Copyright 2004, 2005, 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUDITD_EVENT_H +#define AUDITD_EVENT_H + +#include "libaudit.h" + +typedef void (*ack_func_type)(void *ack_data, const unsigned char *header, const char *msg); + +struct auditd_reply_list { + struct audit_reply reply; + struct auditd_reply_list *next; + ack_func_type ack_func; + void *ack_data; + unsigned long sequence_id; +}; + +#include "auditd-config.h" + +void shutdown_events(void); +int init_event(struct daemon_conf *config); +void resume_logging(void); +void enqueue_event(struct auditd_reply_list *rep); +void enqueue_formatted_event(char *msg, ack_func_type ack_func, void *ack_data, uint32_t sequence_id); +void *consumer_thread_main(void *arg); + +#endif + diff --git a/framework/src/audit/src/auditd-listen.c b/framework/src/audit/src/auditd-listen.c new file mode 100644 index 00000000..d1977c63 --- /dev/null +++ b/framework/src/audit/src/auditd-listen.c @@ -0,0 +1,1065 @@ +/* auditd-listen.c -- + * Copyright 2008,2009,2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * DJ Delorie + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* O_NOFOLLOW needs gnu defined */ +#include +#include +#include /* INT_MAX */ +#include +#include +#include +#include +#ifdef HAVE_LIBWRAP +#include +#endif +#ifdef USE_GSSAPI +#include +#include +#include +#endif +#include "libaudit.h" +#include "auditd-event.h" +#include "auditd-config.h" +#include "private.h" + +#include "ev.h" + +extern volatile int stop; +extern int send_audit_event(int type, const char *str); +#define DEFAULT_BUF_SZ 192 + +typedef struct ev_tcp { + struct ev_io io; + struct sockaddr_in addr; + struct ev_tcp *next, *prev; + unsigned int bufptr; + int client_active; +#ifdef USE_GSSAPI + /* This holds the negotiated security context for this client. */ + gss_ctx_id_t gss_context; + char *remote_name; + int remote_name_len; +#endif + unsigned char buffer [MAX_AUDIT_MESSAGE_LENGTH + 17]; +} ev_tcp; + +static int listen_socket; +static struct ev_io tcp_listen_watcher; +static struct ev_periodic periodic_watcher; +static int min_port, max_port, max_per_addr; +static int use_libwrap = 1; +#ifdef USE_GSSAPI +/* This is used to hold our own private key. */ +static gss_cred_id_t server_creds; +static char *my_service_name, *my_gss_realm; +static int use_gss = 0; +static char msgbuf[MAX_AUDIT_MESSAGE_LENGTH + 1]; +#endif + +static struct ev_tcp *client_chain = NULL; + +static char *sockaddr_to_ipv4(struct sockaddr_in *addr) +{ + unsigned char *uaddr = (unsigned char *)&(addr->sin_addr); + static char buf[40]; + + snprintf(buf, sizeof(buf), "%u.%u.%u.%u", + uaddr[0], uaddr[1], uaddr[2], uaddr[3]); + return buf; +} + +static char *sockaddr_to_addr4(struct sockaddr_in *addr) +{ + unsigned char *uaddr = (unsigned char *)&(addr->sin_addr); + static char buf[40]; + + snprintf(buf, sizeof(buf), "%u.%u.%u.%u:%u", + uaddr[0], uaddr[1], uaddr[2], uaddr[3], + ntohs (addr->sin_port)); + return buf; +} + +static void set_close_on_exec (int fd) +{ + int flags = fcntl (fd, F_GETFD); + if (flags == -1) + flags = 0; + flags |= FD_CLOEXEC; + fcntl (fd, F_SETFD, flags); +} + +static void release_client(struct ev_tcp *client) +{ + char emsg[DEFAULT_BUF_SZ]; + + snprintf(emsg, sizeof(emsg), "addr=%s port=%d res=success", + sockaddr_to_ipv4(&client->addr), ntohs (client->addr.sin_port)); + send_audit_event(AUDIT_DAEMON_CLOSE, emsg); +#ifdef USE_GSSAPI + if (client->remote_name) + free (client->remote_name); +#endif + shutdown(client->io.fd, SHUT_RDWR); + close(client->io.fd); + if (client_chain == client) + client_chain = client->next; + if (client->next) + client->next->prev = client->prev; + if (client->prev) + client->prev->next = client->next; +} + +static void close_client(struct ev_tcp *client) +{ + release_client (client); + free (client); +} + +static int ar_write (int sock, const void *buf, int len) +{ + int rc = 0, w; + while (len > 0) { + do { + w = write(sock, buf, len); + } while (w < 0 && errno == EINTR); + if (w < 0) + return w; + if (w == 0) + break; + rc += w; + len -= w; + buf = (const void *)((const char *)buf + w); + } + return rc; +} + +#ifdef USE_GSSAPI +static int ar_read (int sock, void *buf, int len) +{ + int rc = 0, r; + while (len > 0) { + do { + r = read(sock, buf, len); + } while (r < 0 && errno == EINTR); + if (r < 0) + return r; + if (r == 0) + break; + rc += r; + len -= r; + buf = (void *)((char *)buf + r); + } + return rc; +} + + +/* Communications under GSS is done by token exchanges. Each "token" + may contain a message, perhaps signed, perhaps encrypted. The + messages within are what we're interested in, but the network sees + the tokens. The protocol we use for transferring tokens is to send + the length first, four bytes MSB first, then the token data. We + return nonzero on error. */ +static int recv_token (int s, gss_buffer_t tok) +{ + int ret; + unsigned char lenbuf[4]; + unsigned int len; + + ret = ar_read(s, (char *) lenbuf, 4); + if (ret < 0) { + audit_msg(LOG_ERR, "GSS-API error reading token length"); + return -1; + } else if (!ret) { + return 0; + } else if (ret != 4) { + audit_msg(LOG_ERR, "GSS-API error reading token length"); + return -1; + } + + len = ((lenbuf[0] << 24) + | (lenbuf[1] << 16) + | (lenbuf[2] << 8) + | lenbuf[3]); + if (len > MAX_AUDIT_MESSAGE_LENGTH) { + audit_msg(LOG_ERR, + "GSS-API error: event length excedes MAX_AUDIT_LENGTH"); + return -1; + } + tok->length = len; + + tok->value = (char *) malloc(tok->length ? tok->length : 1); + if (tok->length && tok->value == NULL) { + audit_msg(LOG_ERR, "Out of memory allocating token data"); + return -1; + } + + ret = ar_read(s, (char *) tok->value, tok->length); + if (ret < 0) { + audit_msg(LOG_ERR, "GSS-API error reading token data"); + free(tok->value); + return -1; + } else if (ret != (int) tok->length) { + audit_msg(LOG_ERR, "GSS-API error reading token data"); + free(tok->value); + return -1; + } + + return 1; +} + +/* Same here. */ +int send_token(int s, gss_buffer_t tok) +{ + int ret; + unsigned char lenbuf[4]; + unsigned int len; + + if (tok->length > 0xffffffffUL) + return -1; + len = tok->length; + lenbuf[0] = (len >> 24) & 0xff; + lenbuf[1] = (len >> 16) & 0xff; + lenbuf[2] = (len >> 8) & 0xff; + lenbuf[3] = len & 0xff; + + ret = ar_write(s, (char *) lenbuf, 4); + if (ret < 0) { + audit_msg(LOG_ERR, "GSS-API error sending token length"); + return -1; + } else if (ret != 4) { + audit_msg(LOG_ERR, "GSS-API error sending token length"); + return -1; + } + + ret = ar_write(s, tok->value, tok->length); + if (ret < 0) { + audit_msg(LOG_ERR, "GSS-API error sending token data"); + return -1; + } else if (ret != (int) tok->length) { + audit_msg(LOG_ERR, "GSS-API error sending token data"); + return -1; + } + + return 0; +} + + +static void gss_failure_2 (const char *msg, int status, int type) +{ + OM_uint32 message_context = 0; + OM_uint32 min_status = 0; + gss_buffer_desc status_string; + + do { + gss_display_status (&min_status, + status, + type, + GSS_C_NO_OID, + &message_context, + &status_string); + + audit_msg (LOG_ERR, "GSS error: %s: %s", + msg, (char *)status_string.value); + + gss_release_buffer(&min_status, &status_string); + } while (message_context != 0); +} + +static void gss_failure (const char *msg, int major_status, int minor_status) +{ + gss_failure_2 (msg, major_status, GSS_C_GSS_CODE); + if (minor_status) + gss_failure_2 (msg, minor_status, GSS_C_MECH_CODE); +} + +#define KCHECK(x,f) if (x) { \ + const char *kstr = krb5_get_error_message(kcontext, x); \ + audit_msg(LOG_ERR, "krb5 error: %s in %s\n", kstr, f); \ + krb5_free_error_message(kcontext, kstr); \ + return -1; } + +/* These are our private credentials, which come from a key file on + our server. They are aquired once, at program start. */ +static int server_acquire_creds(const char *service_name, + gss_cred_id_t *server_creds) +{ + gss_buffer_desc name_buf; + gss_name_t server_name; + OM_uint32 major_status, minor_status; + + krb5_context kcontext = NULL; + int krberr; + + my_service_name = strdup (service_name); + name_buf.value = (char *)service_name; + name_buf.length = strlen(name_buf.value) + 1; + major_status = gss_import_name(&minor_status, &name_buf, + (gss_OID) gss_nt_service_name, + &server_name); + if (major_status != GSS_S_COMPLETE) { + gss_failure("importing name", major_status, minor_status); + return -1; + } + + major_status = gss_acquire_cred(&minor_status, + server_name, GSS_C_INDEFINITE, + GSS_C_NULL_OID_SET, GSS_C_ACCEPT, + server_creds, NULL, NULL); + if (major_status != GSS_S_COMPLETE) { + gss_failure("acquiring credentials", + major_status, minor_status); + return -1; + } + + (void) gss_release_name(&minor_status, &server_name); + + krberr = krb5_init_context (&kcontext); + KCHECK (krberr, "krb5_init_context"); + krberr = krb5_get_default_realm (kcontext, &my_gss_realm); + KCHECK (krberr, "krb5_get_default_realm"); + + audit_msg(LOG_DEBUG, "GSS creds for %s acquired", service_name); + + return 0; +} + +/* This is where we negotiate a security context with the client. In + the case of Kerberos, this is where the key exchange happens. + FIXME: While everything else is strictly nonblocking, this + negotiation blocks. */ +static int negotiate_credentials (ev_tcp *io) +{ + gss_buffer_desc send_tok, recv_tok; + gss_name_t client; + OM_uint32 maj_stat, min_stat, acc_sec_min_stat; + gss_ctx_id_t *context; + OM_uint32 sess_flags; + char *slashptr, *atptr; + + context = & io->gss_context; + *context = GSS_C_NO_CONTEXT; + + maj_stat = GSS_S_CONTINUE_NEEDED; + do { + /* STEP 1 - get a token from the client. */ + + if (recv_token(io->io.fd, &recv_tok) <= 0) { + audit_msg(LOG_ERR, + "TCP session from %s will be closed, error ignored", + sockaddr_to_addr4(&io->addr)); + return -1; + } + if (recv_tok.length == 0) + continue; + + /* STEP 2 - let GSS process that token. */ + + maj_stat = gss_accept_sec_context(&acc_sec_min_stat, + context, server_creds, + &recv_tok, + GSS_C_NO_CHANNEL_BINDINGS, &client, + NULL, &send_tok, &sess_flags, + NULL, NULL); + if (recv_tok.value) { + free(recv_tok.value); + recv_tok.value = NULL; + } + if (maj_stat != GSS_S_COMPLETE + && maj_stat != GSS_S_CONTINUE_NEEDED) { + gss_release_buffer(&min_stat, &send_tok); + if (*context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&min_stat, context, + GSS_C_NO_BUFFER); + gss_failure("accepting context", maj_stat, + acc_sec_min_stat); + return -1; + } + + /* STEP 3 - send any tokens to the client that GSS may + ask us to send. */ + + if (send_tok.length != 0) { + if (send_token(io->io.fd, &send_tok) < 0) { + gss_release_buffer(&min_stat, &send_tok); + audit_msg(LOG_ERR, + "TCP session from %s will be closed, error ignored", + sockaddr_to_addr4(&io->addr)); + if (*context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&min_stat, + context, GSS_C_NO_BUFFER); + return -1; + } + gss_release_buffer(&min_stat, &send_tok); + } + } while (maj_stat == GSS_S_CONTINUE_NEEDED); + + maj_stat = gss_display_name(&min_stat, client, &recv_tok, NULL); + gss_release_name(&min_stat, &client); + + if (maj_stat != GSS_S_COMPLETE) { + gss_failure("displaying name", maj_stat, min_stat); + return -1; + } + + audit_msg(LOG_INFO, "GSS-API Accepted connection from: %s", + (char *)recv_tok.value); + io->remote_name = strdup (recv_tok.value); + io->remote_name_len = strlen (recv_tok.value); + gss_release_buffer(&min_stat, &recv_tok); + + slashptr = strchr (io->remote_name, '/'); + atptr = strchr (io->remote_name, '@'); + + if (!slashptr || !atptr) { + audit_msg(LOG_ERR, "Invalid GSS name from remote client: %s", + io->remote_name); + return -1; + } + + *slashptr = 0; + if (strcmp (io->remote_name, my_service_name)) { + audit_msg(LOG_ERR, "Unauthorized GSS client name: %s (not %s)", + io->remote_name, my_service_name); + return -1; + } + *slashptr = '/'; + + if (strcmp (atptr+1, my_gss_realm)) { + audit_msg(LOG_ERR, "Unauthorized GSS client realm: %s (not %s)", + atptr+1, my_gss_realm); + return -1; + } + + return 0; +} +#endif /* USE_GSSAPI */ + +/* This is called from auditd-event after the message has been logged. + The header is already filled in. */ +static void client_ack (void *ack_data, const unsigned char *header, + const char *msg) +{ + ev_tcp *io = (ev_tcp *)ack_data; +#ifdef USE_GSSAPI + if (use_gss) { + OM_uint32 major_status, minor_status; + gss_buffer_desc utok, etok; + int rc, mlen; + + mlen = strlen (msg); + utok.length = AUDIT_RMW_HEADER_SIZE + mlen; + utok.value = malloc (utok.length + 1); + + memcpy (utok.value, header, AUDIT_RMW_HEADER_SIZE); + memcpy (utok.value+AUDIT_RMW_HEADER_SIZE, msg, mlen); + + /* Wrapping the message creates a token for the + client. Then we just have to worry about sending + the token. */ + + major_status = gss_wrap (&minor_status, + io->gss_context, + 1, + GSS_C_QOP_DEFAULT, + &utok, + NULL, + &etok); + if (major_status != GSS_S_COMPLETE) { + gss_failure("encrypting message", major_status, + minor_status); + free (utok.value); + return; + } + // FIXME: What were we going to do with rc? + rc = send_token (io->io.fd, &etok); + free (utok.value); + (void) gss_release_buffer(&minor_status, &etok); + + return; + } +#endif + // Send the header and a text error message if it exists + ar_write (io->io.fd, header, AUDIT_RMW_HEADER_SIZE); + if (msg[0]) + ar_write (io->io.fd, msg, strlen(msg)); +} + +static void client_message (struct ev_tcp *io, unsigned int length, + unsigned char *header) +{ + unsigned char ch; + uint32_t type, mlen, seq; + int hver, mver; + + if (AUDIT_RMW_IS_MAGIC (header, length)) { + AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, mlen, seq) + + ch = header[length]; + header[length] = 0; + if (length > 1 && header[length-1] == '\n') + header[length-1] = 0; + if (type == AUDIT_RMW_TYPE_HEARTBEAT) { + unsigned char ack[AUDIT_RMW_HEADER_SIZE]; + AUDIT_RMW_PACK_HEADER (ack, 0, AUDIT_RMW_TYPE_ACK, + 0, seq); + client_ack (io, ack, ""); + } else + enqueue_formatted_event(header+AUDIT_RMW_HEADER_SIZE, + client_ack, io, seq); + header[length] = ch; + } else { + header[length] = 0; + if (length > 1 && header[length-1] == '\n') + header[length-1] = 0; + enqueue_formatted_event (header, NULL, NULL, 0); + } +} + +static void auditd_tcp_client_handler( struct ev_loop *loop, + struct ev_io *_io, int revents ) +{ + struct ev_tcp *io = (struct ev_tcp *) _io; + int i, r; + int total_this_call = 0; + + io->client_active = 1; + + /* The socket is non-blocking, but we have a limited buffer + size. In the event that we get a packet that's bigger than + our buffer, we need to read it in multiple parts. Thus, we + keep reading/parsing/processing until we run out of ready + data. */ +read_more: + r = read (io->io.fd, + io->buffer + io->bufptr, + MAX_AUDIT_MESSAGE_LENGTH - io->bufptr); + + if (r < 0 && errno == EAGAIN) + r = 0; + + /* We need to keep track of the difference between "no data + * because it's closed" and "no data because we've read it + * all". */ + if (r == 0 && total_this_call > 0) { + return; + } + + /* If the connection is gracefully closed, the first read we + try will return zero. If the connection times out or + otherwise fails, the read will return -1. */ + if (r <= 0) { + if (r < 0) + audit_msg (LOG_WARNING, + "client %s socket closed unexpectedly", + sockaddr_to_addr4(&io->addr)); + + /* There may have been a final message without a LF. */ + if (io->bufptr) { + client_message (io, io->bufptr, io->buffer); + + } + + ev_io_stop (loop, _io); + close_client (io); + return; + } + + total_this_call += r; + +more_messages: +#ifdef USE_GSSAPI + /* If we're using GSS at all, everything will be encrypted, + one record per token. */ + if (use_gss) { + gss_buffer_desc utok, etok; + io->bufptr += r; + uint32_t len; + OM_uint32 major_status, minor_status; + + /* We need at least four bytes to test the length. If + we have more than four bytes, we can tell if we + have a whole token (or more). */ + + if (io->bufptr < 4) + return; + + len = ( ((uint32_t)(io->buffer[0] & 0xFF) << 24) + | ((uint32_t)(io->buffer[1] & 0xFF) << 16) + | ((uint32_t)(io->buffer[2] & 0xFF) << 8) + | (uint32_t)(io->buffer[3] & 0xFF)); + + /* Make sure we got something big enough and not too big */ + if (io->bufptr < 4 + len || len > MAX_AUDIT_MESSAGE_LENGTH) + return; + i = len + 4; + + etok.length = len; + etok.value = io->buffer + 4; + + /* Unwrapping the token gives us the original message, + which we know is already a single record. */ + major_status = gss_unwrap (&minor_status, io->gss_context, + &etok, &utok, NULL, NULL); + + if (major_status != GSS_S_COMPLETE) { + gss_failure("decrypting message", major_status, + minor_status); + } else { + /* client_message() wants to NUL terminate it, + so copy it to a bigger buffer. Plus, we + want to add our own tag. */ + memcpy (msgbuf, utok.value, utok.length); + while (utok.length > 0 && msgbuf[utok.length-1] == '\n') + utok.length --; + snprintf (msgbuf + utok.length, + MAX_AUDIT_MESSAGE_LENGTH - utok.length, + " krb5=%s", io->remote_name); + utok.length += 6 + io->remote_name_len; + client_message (io, utok.length, msgbuf); + gss_release_buffer(&minor_status, &utok); + } + } else +#endif + if (AUDIT_RMW_IS_MAGIC (io->buffer, (io->bufptr+r))) { + uint32_t type, len, seq; + int hver, mver; + unsigned char *header = (unsigned char *)io->buffer; + + io->bufptr += r; + + if (io->bufptr < AUDIT_RMW_HEADER_SIZE) + return; + + AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, len, seq); + + /* Make sure len is not too big */ + if (len > MAX_AUDIT_MESSAGE_LENGTH) + return; + + i = len; + i += AUDIT_RMW_HEADER_SIZE; + + /* See if we have enough bytes to extract the whole message. */ + if (io->bufptr < i) + return; + + /* We have an I-byte message in buffer. Send ACK */ + client_message (io, i, io->buffer); + + } else { + /* At this point, the buffer has IO->BUFPTR+R bytes in it. + The first IO->BUFPTR bytes do not have a LF in them (we've + already checked), we must check the R new bytes. */ + + for (i = io->bufptr; i < io->bufptr + r; i ++) + if (io->buffer [i] == '\n') + break; + + io->bufptr += r; + + /* Check for a partial message, with no LF yet. */ + if (i == io->bufptr) + return; + + i++; + + /* We have an I-byte message in buffer. Send ACK */ + client_message (io, i, io->buffer); + } + + /* Now copy any remaining bytes to the beginning of the + buffer. */ + memmove(io->buffer, io->buffer + i, io->bufptr - i); + io->bufptr -= i; + + /* See if this packet had more than one message in it. */ + if (io->bufptr > 0) { + r = io->bufptr; + io->bufptr = 0; + goto more_messages; + } + + /* Go back and see if there's more data to read. */ + goto read_more; +} + +#ifndef HAVE_LIBWRAP +#define auditd_tcpd_check(s) ({ 0; }) +#else +int allow_severity = LOG_INFO, deny_severity = LOG_NOTICE; +static int auditd_tcpd_check(int sock) +{ + struct request_info request; + + request_init(&request, RQ_DAEMON, "auditd", RQ_FILE, sock, 0); + fromhost(&request); + if (! hosts_access(&request)) + return 1; + return 0; +} +#endif + +/* + * This function counts the number of concurrent connections and returns + * a 1 if there are too many and a 0 otherwise. It assumes the incoming + * connection has not been added to the linked list yet. + */ +static int check_num_connections(struct sockaddr_in *aaddr) +{ + int num = 0; + struct ev_tcp *client = client_chain; + + while (client) { + if (memcmp(&aaddr->sin_addr, &client->addr.sin_addr, + sizeof(struct in_addr)) == 0) { + num++; + if (num >= max_per_addr) + return 1; + } + client = client->next; + } + return 0; +} + +static void auditd_tcp_listen_handler( struct ev_loop *loop, + struct ev_io *_io, int revents ) +{ + int one=1; + int afd; + socklen_t aaddrlen; + struct sockaddr_in aaddr; + struct ev_tcp *client; + char emsg[DEFAULT_BUF_SZ]; + + /* Accept the connection and see where it's coming from. */ + aaddrlen = sizeof(aaddr); + afd = accept (listen_socket, (struct sockaddr *)&aaddr, &aaddrlen); + if (afd == -1) { + audit_msg(LOG_ERR, "Unable to accept TCP connection"); + return; + } + + if (use_libwrap) { + if (auditd_tcpd_check(afd)) { + shutdown(afd, SHUT_RDWR); + close(afd); + audit_msg(LOG_ERR, "TCP connection from %s rejected", + sockaddr_to_addr4(&aaddr)); + snprintf(emsg, sizeof(emsg), + "op=wrap addr=%s port=%d res=no", + sockaddr_to_ipv4(&aaddr), + ntohs (aaddr.sin_port)); + send_audit_event(AUDIT_DAEMON_ACCEPT, emsg); + return; + } + } + + /* Verify it's coming from an authorized port. We assume the firewall + * will block attempts from unauthorized machines. */ + if (min_port > ntohs (aaddr.sin_port) || + ntohs (aaddr.sin_port) > max_port) { + audit_msg(LOG_ERR, "TCP connection from %s rejected", + sockaddr_to_addr4(&aaddr)); + snprintf(emsg, sizeof(emsg), + "op=port addr=%s port=%d res=no", + sockaddr_to_ipv4(&aaddr), + ntohs (aaddr.sin_port)); + send_audit_event(AUDIT_DAEMON_ACCEPT, emsg); + shutdown(afd, SHUT_RDWR); + close(afd); + return; + } + + /* Make sure we don't have too many connections */ + if (check_num_connections(&aaddr)) { + audit_msg(LOG_ERR, "Too many connections from %s - rejected", + sockaddr_to_addr4(&aaddr)); + snprintf(emsg, sizeof(emsg), + "op=dup addr=%s port=%d res=no", + sockaddr_to_ipv4(&aaddr), + ntohs (aaddr.sin_port)); + send_audit_event(AUDIT_DAEMON_ACCEPT, emsg); + shutdown(afd, SHUT_RDWR); + close(afd); + return; + } + + /* Connection is accepted...start setting it up */ + setsockopt(afd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int)); + setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof (int)); + setsockopt(afd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int)); + set_close_on_exec (afd); + + /* Make the client data structure */ + client = (struct ev_tcp *) malloc (sizeof (struct ev_tcp)); + if (client == NULL) { + audit_msg(LOG_CRIT, "Unable to allocate TCP client data"); + snprintf(emsg, sizeof(emsg), + "op=alloc addr=%s port=%d res=no", + sockaddr_to_ipv4(&aaddr), + ntohs (aaddr.sin_port)); + send_audit_event(AUDIT_DAEMON_ACCEPT, emsg); + shutdown(afd, SHUT_RDWR); + close(afd); + return; + } + + memset (client, 0, sizeof (struct ev_tcp)); + client->client_active = 1; + + // Was watching for EV_ERROR, but libev 3.48 took it away + ev_io_init (&(client->io), auditd_tcp_client_handler, afd, EV_READ); + + memcpy (&client->addr, &aaddr, sizeof (struct sockaddr_in)); + +#ifdef USE_GSSAPI + if (use_gss && negotiate_credentials (client)) { + shutdown(afd, SHUT_RDWR); + close(afd); + free(client); + return; + } +#endif + + fcntl(afd, F_SETFL, O_NONBLOCK | O_NDELAY); + ev_io_start (loop, &(client->io)); + + /* Add the new connection to a linked list of active clients. */ + client->next = client_chain; + if (client->next) + client->next->prev = client; + client_chain = client; + + /* And finally log that we accepted the connection */ + snprintf(emsg, sizeof(emsg), + "addr=%s port=%d res=success", sockaddr_to_ipv4(&aaddr), + ntohs (aaddr.sin_port)); + send_audit_event(AUDIT_DAEMON_ACCEPT, emsg); +} + +static void auditd_set_ports(int minp, int maxp, int max_p_addr) +{ + min_port = minp; + max_port = maxp; + max_per_addr = max_p_addr; +} + +static void periodic_handler(struct ev_loop *loop, struct ev_periodic *per, + int revents ) +{ + struct daemon_conf *config = (struct daemon_conf *) per->data; + struct ev_tcp *ev, *next = NULL; + int active; + + if (!config->tcp_client_max_idle) + return; + + for (ev = client_chain; ev; ev = next) { + active = ev->client_active; + ev->client_active = 0; + if (active) + continue; + + audit_msg(LOG_NOTICE, + "client %s idle too long - closing connection\n", + sockaddr_to_addr4(&(ev->addr))); + ev_io_stop (loop, &ev->io); + release_client(ev); + next = ev->next; + free(ev); + } +} + +int auditd_tcp_listen_init ( struct ev_loop *loop, struct daemon_conf *config ) +{ + struct sockaddr_in address; + int one = 1; + + ev_periodic_init (&periodic_watcher, periodic_handler, + 0, config->tcp_client_max_idle, NULL); + periodic_watcher.data = config; + if (config->tcp_client_max_idle) + ev_periodic_start (loop, &periodic_watcher); + + /* If the port is not set, that means we aren't going to + listen for connections. */ + if (config->tcp_listen_port == 0) + return 0; + + listen_socket = socket (AF_INET, SOCK_STREAM, 0); + if (listen_socket < 0) { + audit_msg(LOG_ERR, "Cannot create tcp listener socket"); + return 1; + } + + set_close_on_exec (listen_socket); + setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof (int)); + + memset (&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(config->tcp_listen_port); + address.sin_addr.s_addr = htonl(INADDR_ANY); + + /* This avoids problems if auditd needs to be restarted. */ + setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof (int)); + + if (bind(listen_socket, (struct sockaddr *)&address, sizeof(address))){ + audit_msg(LOG_ERR, + "Cannot bind tcp listener socket to port %ld", + config->tcp_listen_port); + close(listen_socket); + return 1; + } + + listen(listen_socket, config->tcp_listen_queue); + + audit_msg(LOG_DEBUG, "Listening on TCP port %ld", + config->tcp_listen_port); + + ev_io_init (&tcp_listen_watcher, auditd_tcp_listen_handler, + listen_socket, EV_READ); + ev_io_start (loop, &tcp_listen_watcher); + + use_libwrap = config->use_libwrap; + auditd_set_ports(config->tcp_client_min_port, + config->tcp_client_max_port, + config->tcp_max_per_addr); + +#ifdef USE_GSSAPI + if (config->enable_krb5) { + const char *princ = config->krb5_principal; + const char *key_file; + struct stat st; + + if (!princ) + princ = "auditd"; + use_gss = 1; + /* This may fail, but we don't care. */ + unsetenv ("KRB5_KTNAME"); + if (config->krb5_key_file) + key_file = config->krb5_key_file; + else + key_file = "/etc/audit/audit.key"; + setenv ("KRB5_KTNAME", key_file, 1); + + if (stat (key_file, &st) == 0) { + if ((st.st_mode & 07777) != 0400) { + audit_msg (LOG_ERR, + "%s is not mode 0400 (it's %#o) - compromised key?", + key_file, st.st_mode & 07777); + return -1; + } + if (st.st_uid != 0) { + audit_msg (LOG_ERR, + "%s is not owned by root (it's %d) - compromised key?", + key_file, st.st_uid); + return -1; + } + } + + server_acquire_creds(princ, &server_creds); + } +#endif + + return 0; +} + +void auditd_tcp_listen_uninit ( struct ev_loop *loop, + struct daemon_conf *config ) +{ +#ifdef USE_GSSAPI + OM_uint32 status; +#endif + + ev_io_stop ( loop, &tcp_listen_watcher ); + close ( listen_socket ); + +#ifdef USE_GSSAPI + if (use_gss) { + use_gss = 0; + gss_release_cred(&status, &server_creds); + } +#endif + + while (client_chain) { + unsigned char ack[AUDIT_RMW_HEADER_SIZE]; + + AUDIT_RMW_PACK_HEADER (ack, 0, AUDIT_RMW_TYPE_ENDING, 0, 0); + client_ack (client_chain, ack, ""); + ev_io_stop (loop, &client_chain->io); + close_client (client_chain); + } + + if (config->tcp_client_max_idle) + ev_periodic_stop (loop, &periodic_watcher); +} + +static void periodic_reconfigure(struct daemon_conf *config) +{ + struct ev_loop *loop = ev_default_loop (EVFLAG_AUTO); + if (config->tcp_client_max_idle) { + ev_periodic_set (&periodic_watcher, ev_now (loop), + config->tcp_client_max_idle, NULL); + ev_periodic_start (loop, &periodic_watcher); + } else { + ev_periodic_stop (loop, &periodic_watcher); + } +} + +void auditd_tcp_listen_reconfigure ( struct daemon_conf *nconf, + struct daemon_conf *oconf ) +{ + /* Look at network things that do not need restarting */ + if (oconf->tcp_client_min_port != nconf->tcp_client_min_port || + oconf->tcp_client_max_port != nconf->tcp_client_max_port || + oconf->tcp_max_per_addr != nconf->tcp_max_per_addr) { + oconf->tcp_client_min_port = nconf->tcp_client_min_port; + oconf->tcp_client_max_port = nconf->tcp_client_max_port; + oconf->tcp_max_per_addr = nconf->tcp_max_per_addr; + auditd_set_ports(oconf->tcp_client_min_port, + oconf->tcp_client_max_port, + oconf->tcp_max_per_addr); + } + if (oconf->tcp_client_max_idle != nconf->tcp_client_max_idle) { + oconf->tcp_client_max_idle = nconf->tcp_client_max_idle; + periodic_reconfigure(oconf); + } + if (oconf->tcp_listen_port != nconf->tcp_listen_port || + oconf->tcp_listen_queue != nconf->tcp_listen_queue) { + oconf->tcp_listen_port = nconf->tcp_listen_port; + oconf->tcp_listen_queue = nconf->tcp_listen_queue; + // FIXME: need to restart the network stuff + } +} diff --git a/framework/src/audit/src/auditd-listen.h b/framework/src/audit/src/auditd-listen.h new file mode 100644 index 00000000..69f9310a --- /dev/null +++ b/framework/src/audit/src/auditd-listen.h @@ -0,0 +1,55 @@ +/* auditd-config.h -- + * Copyright 2004-2007 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * DJ Delorie + * + */ + +#ifndef AUDITD_LISTEN_H +#define AUDITD_LISTEN_H + +#include "ev.h" + +#ifdef USE_LISTENER +int auditd_tcp_listen_init ( struct ev_loop *loop, struct daemon_conf *config ); +void auditd_tcp_listen_uninit ( struct ev_loop *loop, + struct daemon_conf *config ); +void auditd_tcp_listen_reconfigure ( struct daemon_conf *nconf, + struct daemon_conf *oconf ); +#else +static inline int auditd_tcp_listen_init ( struct ev_loop *loop, + struct daemon_conf *config ) +{ + return 0; +} + +static inline void auditd_tcp_listen_uninit ( struct ev_loop *loop, + struct daemon_conf *config ) +{ + return; +} + +static inline void auditd_tcp_listen_reconfigure ( struct daemon_conf *nconf, + struct daemon_conf *oconf ) +{ + return; +} +#endif /* USE_LISTENER */ + +#endif diff --git a/framework/src/audit/src/auditd-reconfig.c b/framework/src/audit/src/auditd-reconfig.c new file mode 100644 index 00000000..ac3bd030 --- /dev/null +++ b/framework/src/audit/src/auditd-reconfig.c @@ -0,0 +1,128 @@ +/* auditd-reconfig.c -- + * Copyright 2005 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "auditd-event.h" +#include "auditd-config.h" +#include "private.h" + +/* This is the configuration manager code */ +static pthread_t config_thread; +static pthread_mutex_t config_lock; +static void *config_thread_main(void *arg); + +void init_config_manager(void) +{ + pthread_mutex_init(&config_lock, NULL); + audit_msg(LOG_DEBUG, "config_manager init complete"); +} + +int start_config_manager(struct auditd_reply_list *rep) +{ + int retval, rc = 0; + + retval = pthread_mutex_trylock(&config_lock); + if (retval == 0) { + pthread_attr_t detached; + + pthread_attr_init(&detached); + pthread_attr_setdetachstate(&detached, + PTHREAD_CREATE_DETACHED); + + if (pthread_create(&config_thread, &detached, + config_thread_main, rep) < 0) { + audit_msg(LOG_ERR, + "Couldn't create config thread, no config changes"); + free(rep); + pthread_mutex_unlock(&config_lock); + rc = 1; + } + pthread_attr_destroy(&detached); + } else { + audit_msg(LOG_ERR, + "Config thread already running, no config changes"); + free(rep); + rc = 1; + } + return rc; +} + +void shutdown_config(void) +{ + pthread_cancel(config_thread); +} + +static void *config_thread_main(void *arg) +{ + sigset_t sigs; + struct auditd_reply_list *rep = (struct auditd_reply_list *)arg; + struct daemon_conf new_config; + extern int send_audit_event(int type, const char *str); + + /* This is a worker thread. Don't handle signals. */ + sigemptyset(&sigs); + sigaddset(&sigs, SIGALRM); + sigaddset(&sigs, SIGTERM); + sigaddset(&sigs, SIGHUP); + sigaddset(&sigs, SIGUSR1); + sigaddset(&sigs, SIGUSR2); + pthread_sigmask(SIG_SETMASK, &sigs, NULL); + + if (load_config(&new_config, TEST_AUDITD) == 0) { + /* We will re-use the current reply */ + new_config.sender_uid = rep->reply.signal_info->uid; + new_config.sender_pid = rep->reply.signal_info->pid; + if (rep->reply.len > 24) + new_config.sender_ctx = + strdup(rep->reply.signal_info->ctx); + else + new_config.sender_ctx = strdup("?"); + memcpy(rep->reply.msg.data, &new_config, sizeof(new_config)); + rep->reply.conf = (struct daemon_conf *)rep->reply.msg.data; + rep->reply.type = AUDIT_DAEMON_RECONFIG; + enqueue_event(rep); + } else { + // need to send a failed event message + char txt[MAX_AUDIT_MESSAGE_LENGTH]; + snprintf(txt, sizeof(txt), + "reconfig aborted, sending auid=%u pid=%d subj=%s res=failed", + rep->reply.signal_info->uid, + rep->reply.signal_info->pid, + (rep->reply.len > 24) ? + rep->reply.signal_info->ctx : "?"); + send_audit_event(AUDIT_DAEMON_CONFIG, txt); + free_config(&new_config); + free(rep); + } + + pthread_mutex_unlock(&config_lock); + return NULL; +} + diff --git a/framework/src/audit/src/auditd-sendmail.c b/framework/src/audit/src/auditd-sendmail.c new file mode 100644 index 00000000..ab0b901f --- /dev/null +++ b/framework/src/audit/src/auditd-sendmail.c @@ -0,0 +1,116 @@ +/* auditd-sendmail.c -- + * Copyright 2005 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#include "config.h" +#include +#include // for access() +#include +#include +#include +#include "libaudit.h" +#include "private.h" +#include "auditd-config.h" + +extern const char *email_command; +static int safe_popen(pid_t *pid, const char *mail_acct); + +// returns 1 on error & 0 if OK +int sendmail(const char *subject, const char *content, const char *mail_acct) +{ + pid_t pid; + + if (access(email_command, 01) == 0) + { + FILE *mail; + int fd; + + fd = safe_popen(&pid, mail_acct); + if (fd < 0) + return 1; + mail = fdopen(fd, "w"); + if (mail == NULL) { + kill(pid, SIGKILL); + close(fd); + audit_msg(LOG_ERR, "Error - starting mail"); + return 1; + } + + fprintf(mail, "To: %s\n", mail_acct); + fprintf(mail, "From: root\n"); +// fprintf(mail, "X-Sender: %s\n", mail_acct); + fprintf(mail, "Subject: %s\n\n", subject); // End of Header + fprintf(mail, "%s\n", content); + fprintf(mail, ".\n\n"); // Close it up... + fclose(mail); + return 0; + } else + audit_msg(LOG_ERR, "Error - %s isn't executable", + email_command); + return 1; +} + +static int safe_popen(pid_t *pid, const char *mail_acct) +{ + char *argv[4]; + char acct[256]; + int pipe_fd[2]; + struct sigaction sa; + + if (pipe(pipe_fd)) { + audit_msg(LOG_ALERT, + "Audit daemon failed to create pipe while sending email alert"); + return -1; + } + + *pid = fork(); + if (*pid < 0) { + close(pipe_fd[0]); + close(pipe_fd[1]); + audit_msg(LOG_ALERT, + "Audit daemon failed to fork while sending email alert"); + return -1; + } + if (*pid) { /* Parent */ + close(pipe_fd[0]); // adjust pipe + return pipe_fd[1]; + } + /* Child */ + sigfillset (&sa.sa_mask); + sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); + + close(pipe_fd[1]); // adjust pipe + dup2(pipe_fd[0], 0); + + /* Make email acct param */ + snprintf(acct, sizeof(acct), "-f%s", mail_acct); + + /* Stuff arg list */ + argv[0] = (char *)email_command; + argv[1] = (char *)"-t"; + argv[2] = acct; + argv[3] = NULL; + execve(email_command, argv, NULL); + audit_msg(LOG_ALERT, "Audit daemon failed to exec %s", email_command); + exit(1); +} + diff --git a/framework/src/audit/src/auditd.c b/framework/src/audit/src/auditd.c new file mode 100644 index 00000000..5afebac2 --- /dev/null +++ b/framework/src/audit/src/auditd.c @@ -0,0 +1,917 @@ +/* auditd.c -- + * Copyright 2004-09,2011,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Rickard E. (Rik) Faith + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libaudit.h" +#include "auditd-event.h" +#include "auditd-config.h" +#include "auditd-dispatch.h" +#include "auditd-listen.h" +#include "private.h" + +#include "ev.h" + +#define EV_STOP() ev_unloop (ev_default_loop (EVFLAG_AUTO), EVUNLOOP_ALL), stop = 1; + +#define DEFAULT_BUF_SZ 448 +#define DMSG_SIZE (DEFAULT_BUF_SZ + 48) +#define SUCCESS 0 +#define FAILURE 1 +#define SUBJ_LEN 4097 + +/* Global Data */ +volatile int stop = 0; + +/* Local data */ +static int fd = -1; +static struct daemon_conf config; +static const char *pidfile = "/var/run/auditd.pid"; +static int init_pipe[2]; +static int do_fork = 1; +static struct auditd_reply_list *rep = NULL; +static int hup_info_requested = 0; +static int usr1_info_requested = 0, usr2_info_requested = 0; +static char subj[SUBJ_LEN]; + +/* Local function prototypes */ +int send_audit_event(int type, const char *str); +static void close_down(void); +static void clean_exit(void); +static int get_reply(int fd, struct audit_reply *rep, int seq); +static char *getsubj(char *subj); + +enum startup_state {startup_disable=0, startup_enable, startup_nochange, + startup_INVALID}; +static const char *startup_states[] = {"disable", "enable", "nochange"}; + +/* + * Output a usage message + */ +static void usage(void) +{ + fprintf(stderr, "Usage: auditd [-f] [-l] [-n] [-s %s|%s|%s]\n", + startup_states[startup_disable], + startup_states[startup_enable], + startup_states[startup_nochange]); + + exit(2); +} + + +/* + * SIGTERM handler + */ +static void term_handler(struct ev_loop *loop, struct ev_signal *sig, + int revents) +{ + EV_STOP (); +} + +/* + * Used with sigalrm to force exit + */ +static void thread_killer( int sig ) +{ + exit(0); +} + +/* + * Used with sigalrm to force exit + */ +static void hup_handler( struct ev_loop *loop, struct ev_signal *sig, int revents ) +{ + int rc; + + rc = audit_request_signal_info(fd); + if (rc < 0) + send_audit_event(AUDIT_DAEMON_CONFIG, + "auditd error getting hup info - no change, sending auid=? pid=? subj=? res=failed"); + else + hup_info_requested = 1; +} + +/* + * Used to force log rotation + */ +static void user1_handler(struct ev_loop *loop, struct ev_signal *sig, + int revents) +{ + int rc; + + rc = audit_request_signal_info(fd); + if (rc < 0) + send_audit_event(AUDIT_DAEMON_ROTATE, + "auditd error getting usr1 info - no change, sending auid=? pid=? subj=? res=failed"); + else + usr1_info_requested = 1; +} + +/* + * Used to resume logging + */ +static void user2_handler( struct ev_loop *loop, struct ev_signal *sig, int revents ) +{ + int rc; + + rc = audit_request_signal_info(fd); + if (rc < 0) { + resume_logging(); + send_audit_event(AUDIT_DAEMON_RESUME, + "auditd resuming logging, sending auid=? pid=? subj=? res=success"); + } else + usr2_info_requested = 1; +} + +/* + * Used with email alerts to cleanup + */ +static void child_handler(struct ev_loop *loop, struct ev_signal *sig, + int revents) +{ + int pid; + + while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { + if (pid == dispatcher_pid()) + dispatcher_reaped(); + } +} + +static void distribute_event(struct auditd_reply_list *rep) +{ + int attempt = 0; + + /* Make first attempt to send to plugins */ + if (dispatch_event(&rep->reply, attempt) == 1) + attempt++; /* Failed sending, retry after writing to disk */ + + /* End of Event is for realtime interface - skip local logging of it */ + if (rep->reply.type != AUDIT_EOE) { + int yield = rep->reply.type <= AUDIT_LAST_DAEMON && + rep->reply.type >= AUDIT_FIRST_DAEMON ? 1 : 0; + /* Write to local disk */ + enqueue_event(rep); + if (yield) { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 2 * 1000 * 1000; // 2 milliseconds + nanosleep(&ts, NULL); // Let other thread try to log it + } + } else + free(rep); // This function takes custody of the memory + + // FIXME: This is commented out since it fails to work. The + // problem is that the logger thread free's the buffer. Probably + // need a way to flag in the buffer if logger thread should free or + // move the free to this function. + + /* Last chance to send...maybe the pipe is empty now. */ +// if (attempt) +// dispatch_event(&rep->reply, attempt); +} + +/* + * This function is used to send start, stop, and abort messages + * to the audit log. + */ +static unsigned seq_num = 0; +int send_audit_event(int type, const char *str) +{ + struct auditd_reply_list *rep; + struct timeval tv; + + if ((rep = malloc(sizeof(*rep))) == NULL) { + audit_msg(LOG_ERR, "Cannot allocate audit reply"); + return 1; + } + + rep->reply.type = type; + rep->reply.message = (char *)malloc(DMSG_SIZE); + if (rep->reply.message == NULL) { + free(rep); + audit_msg(LOG_ERR, "Cannot allocate local event message"); + return 1; + } + if (seq_num == 0) { + srand(time(NULL)); + seq_num = rand()%10000; + } else + seq_num++; + if (gettimeofday(&tv, NULL) == 0) { + rep->reply.len = snprintf((char *)rep->reply.message, + DMSG_SIZE, "audit(%lu.%03u:%u): %s", + tv.tv_sec, (unsigned)(tv.tv_usec/1000), seq_num, str); + } else { + rep->reply.len = snprintf((char *)rep->reply.message, + DMSG_SIZE, "audit(%lu.%03u:%u): %s", + (unsigned long)time(NULL), 0, seq_num, str); + } + if (rep->reply.len > DMSG_SIZE) + rep->reply.len = DMSG_SIZE; + + distribute_event(rep); + return 0; +} + +static int write_pid_file(void) +{ + int pidfd, len; + char val[16]; + + len = snprintf(val, sizeof(val), "%u\n", getpid()); + if (len <= 0) { + audit_msg(LOG_ERR, "Pid error (%s)", strerror(errno)); + pidfile = 0; + return 1; + } + pidfd = open(pidfile, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644); + if (pidfd < 0) { + audit_msg(LOG_ERR, "Unable to set pidfile (%s)", + strerror(errno)); + pidfile = 0; + return 1; + } + if (write(pidfd, val, (unsigned int)len) != len) { + audit_msg(LOG_ERR, "Unable to write pidfile (%s)", + strerror(errno)); + close(pidfd); + pidfile = 0; + return 1; + } + close(pidfd); + return 0; +} + +static void avoid_oom_killer(void) +{ + int oomfd, len, rc; + char *score = NULL; + + /* New kernels use different technique */ + if ((oomfd = open("/proc/self/oom_score_adj", + O_NOFOLLOW | O_WRONLY)) >= 0) { + score = "-1000"; + } else if ((oomfd = open("/proc/self/oom_adj", + O_NOFOLLOW | O_WRONLY)) >= 0) { + score = "-17"; + } else { + audit_msg(LOG_NOTICE, "Cannot open out of memory adjuster"); + return; + } + + len = strlen(score); + rc = write(oomfd, score, len); + if (rc != len) + audit_msg(LOG_NOTICE, "Unable to adjust out of memory score"); + + close(oomfd); +} + +/* + * This function will take care of becoming a daemon. The parent + * will wait until the child notifies it by writing into a special + * pipe to signify that it successfully initialized. This prevents + * a race in the init script where rules get loaded before the daemon + * is ready and they wind up in syslog. The child returns 0 on success + * and nonzero on failure. The parent returns nonzero on failure. On + * success, the parent calls _exit with 0. + */ +static int become_daemon(void) +{ + int fd, rc; + pid_t pid; + int status; + + if (do_fork) { + if (pipe(init_pipe) || + fcntl(init_pipe[0], F_SETFD, FD_CLOEXEC) || + fcntl(init_pipe[0], F_SETFD, FD_CLOEXEC)) + return -1; + pid = fork(); + } else + pid = 0; + + switch (pid) + { + case 0: + /* No longer need this... */ + if (do_fork) + close(init_pipe[0]); + + /* Open stdin,out,err to /dev/null */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) { + audit_msg(LOG_ERR, "Cannot open /dev/null"); + return -1; + } + if ((dup2(fd, 0) < 0) || (dup2(fd, 1) < 0) || + (dup2(fd, 2) < 0)) { + audit_msg(LOG_ERR, + "Cannot reassign descriptors to /dev/null"); + close(fd); + return -1; + } + close(fd); + + /* Change to '/' */ + rc = chdir("/"); + if (rc < 0) { + audit_msg(LOG_ERR, + "Cannot change working directory to /"); + return -1; + } + + /* Become session/process group leader */ + setsid(); + break; + case -1: + return -1; + break; + default: + /* Wait for the child to say its done */ + rc = read(init_pipe[0], &status, sizeof(status)); + if (rc < 0) + return -1; + + /* Success - die a happy death */ + if (status == SUCCESS) + _exit(0); + else + return -1; + break; + } + + return 0; +} + +static void tell_parent(int status) +{ + int rc; + + if (config.daemonize != D_BACKGROUND || do_fork == 0) + return; + do { + rc = write(init_pipe[1], &status, sizeof(status)); + } while (rc < 0 && errno == EINTR); +} + +static void netlink_handler(struct ev_loop *loop, struct ev_io *io, + int revents) +{ + if (rep == NULL) { + if ((rep = malloc(sizeof(*rep))) == NULL) { + char emsg[DEFAULT_BUF_SZ]; + if (*subj) + snprintf(emsg, sizeof(emsg), + "auditd error halt, auid=%u pid=%d subj=%s res=failed", + audit_getloginuid(), getpid(), subj); + else + snprintf(emsg, sizeof(emsg), + "auditd error halt, auid=%u pid=%d res=failed", + audit_getloginuid(), getpid()); + EV_STOP (); + send_audit_event(AUDIT_DAEMON_ABORT, emsg); + audit_msg(LOG_ERR, + "Cannot allocate audit reply, exiting"); + close_down(); + if (pidfile) + unlink(pidfile); + shutdown_dispatcher(); + return; + } + } + if (audit_get_reply(fd, &rep->reply, + GET_REPLY_NONBLOCKING, 0) > 0) { + switch (rep->reply.type) + { /* For now dont process these */ + case NLMSG_NOOP: + case NLMSG_DONE: + case NLMSG_ERROR: + case AUDIT_GET: /* Or these */ + case AUDIT_LIST_RULES: + case AUDIT_FIRST_DAEMON...AUDIT_LAST_DAEMON: + break; + case AUDIT_SIGNAL_INFO: + if (hup_info_requested) { + audit_msg(LOG_DEBUG, + "HUP detected, starting config manager"); + if (start_config_manager(rep)) { + send_audit_event( + AUDIT_DAEMON_CONFIG, + "auditd error getting hup info - no change," + " sending auid=? pid=? subj=? res=failed"); + } + rep = NULL; + hup_info_requested = 0; + } else if (usr1_info_requested) { + char usr1[MAX_AUDIT_MESSAGE_LENGTH]; + if (rep->reply.len == 24) { + snprintf(usr1, sizeof(usr1), + "auditd sending auid=? pid=? subj=?"); + } else { + snprintf(usr1, sizeof(usr1), + "auditd sending auid=%u pid=%d subj=%s", + rep->reply.signal_info->uid, + rep->reply.signal_info->pid, + rep->reply.signal_info->ctx); + } + send_audit_event(AUDIT_DAEMON_ROTATE, usr1); + usr1_info_requested = 0; + } else if (usr2_info_requested) { + char usr2[MAX_AUDIT_MESSAGE_LENGTH]; + if (rep->reply.len == 24) { + snprintf(usr2, sizeof(usr2), + "auditd resuming logging, " + "sending auid=? pid=? subj=? " + "res=success"); + } else { + snprintf(usr2, sizeof(usr2), + "auditd resuming logging, " + "sending auid=%u pid=%d subj=%s res=success", + rep->reply.signal_info->uid, + rep->reply.signal_info->pid, + rep->reply.signal_info->ctx); + } + resume_logging(); + send_audit_event(AUDIT_DAEMON_RESUME, usr2); + usr2_info_requested = 0; + } + break; + default: + distribute_event(rep); + rep = NULL; + break; + } + } else { + if (errno == EFBIG) { + // FIXME do err action + } + } +} + +int main(int argc, char *argv[]) +{ + struct sigaction sa; + struct rlimit limit; + int i, c, rc; + int opt_foreground = 0, opt_allow_links = 0; + enum startup_state opt_startup = startup_enable; + extern char *optarg; + extern int optind; + struct ev_loop *loop; + struct ev_io netlink_watcher; + struct ev_signal sigterm_watcher; + struct ev_signal sighup_watcher; + struct ev_signal sigusr1_watcher; + struct ev_signal sigusr2_watcher; + struct ev_signal sigchld_watcher; + + /* Get params && set mode */ + while ((c = getopt(argc, argv, "flns:")) != -1) { + switch (c) { + case 'f': + opt_foreground = 1; + break; + case 'l': + opt_allow_links=1; + break; + case 'n': + do_fork = 0; + break; + case 's': + for (i=0; i 0) { + struct audit_reply trep; + + rc = get_reply(fd, &trep, rc); + if (rc > 0) { + char txt[MAX_AUDIT_MESSAGE_LENGTH]; + snprintf(txt, sizeof(txt), + "auditd normal halt, sending auid=%u " + "pid=%d subj=%s res=success", + trep.signal_info->uid, + trep.signal_info->pid, + trep.signal_info->ctx); + send_audit_event(AUDIT_DAEMON_END, txt); + } + } + if (rc <= 0) + send_audit_event(AUDIT_DAEMON_END, + "auditd normal halt, sending auid=? " + "pid=? subj=? res=success"); + free(rep); + + // Tear down IO watchers Part 2 + ev_io_stop (loop, &netlink_watcher); + + // Give DAEMON_END event a little time to be sent in case + // of remote logging + usleep(10000); // 10 milliseconds + shutdown_dispatcher(); + + // Tear down IO watchers Part 3 + ev_signal_stop (loop, &sigchld_watcher); + + close_down(); + free_config(&config); + ev_default_destroy(); + + return 0; +} + +static void close_down(void) +{ + struct sigaction sa; + + /* We are going down. Give the event thread a chance to shutdown. + Just in case it hangs, set a timer to get us out of trouble. */ + sa.sa_flags = 0 ; + sigemptyset( &sa.sa_mask ) ; + sa.sa_handler = thread_killer; + sigaction( SIGALRM, &sa, NULL ); + shutdown_events(); +} + + +/* + * A clean exit means : + * 1) we log that we are going down + * 2) deregister with kernel + * 3) close the netlink socket + */ +static void clean_exit(void) +{ + audit_msg(LOG_INFO, "The audit daemon is exiting."); + if (fd >= 0) { + audit_set_pid(fd, 0, WAIT_NO); + audit_close(fd); + } + if (pidfile) + unlink(pidfile); + closelog(); +} + +/* + * This function is used to get the reply for term info. + * Returns 1 on success & -1 on failure. + */ +static int get_reply(int fd, struct audit_reply *rep, int seq) +{ + int rc, i; + int timeout = 30; /* tenths of seconds */ + + for (i = 0; i < timeout; i++) { + struct timeval t; + fd_set read_mask; + + t.tv_sec = 0; + t.tv_usec = 100000; /* .1 second */ + FD_ZERO(&read_mask); + FD_SET(fd, &read_mask); + do { + rc = select(fd+1, &read_mask, NULL, NULL, &t); + } while (rc < 0 && errno == EINTR); + rc = audit_get_reply(fd, rep, + GET_REPLY_NONBLOCKING, 0); + if (rc > 0) { + /* Don't make decisions based on wrong packet */ + if (rep->nlh->nlmsg_seq != seq) + continue; + + /* If its not what we are expecting, keep looping */ + if (rep->type == AUDIT_SIGNAL_INFO) + return 1; + + /* If we get done or error, break out */ + if (rep->type == NLMSG_DONE || rep->type == NLMSG_ERROR) + break; + } + } + return -1; +} + +//get the subj of the daemon +static char *getsubj(char *subj) +{ + pid_t pid = getpid(); + char filename[48]; + ssize_t num_read; + int fd; + + snprintf(filename, sizeof(filename), "/proc/%u/attr/current", pid); + fd = open(filename, O_RDONLY); + if(fd == -1) { + subj[0] = 0; + return NULL; + } + do { + num_read = read(fd, subj, SUBJ_LEN-1); + } while (num_read < 0 && errno == EINTR); + close(fd); + if(num_read <= 0) { + subj[0] = 0; + return NULL; + } + subj[num_read] = '\0'; + return subj; +} + diff --git a/framework/src/audit/src/aureport-options.c b/framework/src/audit/src/aureport-options.c new file mode 100644 index 00000000..7508417f --- /dev/null +++ b/framework/src/audit/src/aureport-options.c @@ -0,0 +1,722 @@ +/* aureport-options.c - parse commandline options and configure aureport + * Copyright 2005-08,2010-11,2014 Red Hat Inc., Durham, North Carolina. + * Copyright (c) 2011 IBM Corp. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Marcelo Henrique Cerri + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "aureport-options.h" +#include "ausearch-time.h" +#include "libaudit.h" + + +/* Global vars that will be accessed by the main program */ +char *user_file = NULL; +int force_logs = 0; +int no_config = 0; + +/* These are for compatibility with parser */ +unsigned int event_id = -1; +uid_t event_uid = -1, event_loginuid = -2, event_euid = -1; +gid_t event_gid = -1, event_egid = -1; +slist *event_node_list = NULL; +const char *event_key = NULL; +const char *event_filename = NULL; +const char *event_exe = NULL; +const char *event_comm = NULL; +const char *event_hostname = NULL; +const char *event_terminal = NULL; +const char *event_subject = NULL; +const char *event_object = NULL; +const char *event_uuid = NULL; +const char *event_vmname = NULL; +long long event_exit = 0; +int event_exit_is_set = 0; +int event_ppid = -1, event_session_id = -2; +int event_debug = 0, event_machine = -1; + +/* These are used by aureport */ +const char *dummy = "dummy"; +report_type_t report_type = RPT_UNSET; +report_det_t report_detail = D_UNSET; +report_t report_format = RPT_DEFAULT; +failed_t event_failed = F_BOTH; +conf_act_t event_conf_act = C_NEITHER; +success_t event_success = S_SUCCESS; +int event_pid = 0; + +struct nv_pair { + int value; + const char *name; +}; + +enum { R_INFILE, R_TIME_END, R_TIME_START, R_VERSION, R_SUMMARY, R_LOG_TIMES, + R_CONFIGS, R_LOGINS, R_USERS, R_TERMINALS, R_HOSTS, R_EXES, R_FILES, + R_AVCS, R_SYSCALLS, R_PIDS, R_EVENTS, R_ACCT_MODS, + R_INTERPRET, R_HELP, R_ANOMALY, R_RESPONSE, R_SUMMARY_DET, R_CRYPTO, + R_MAC, R_FAILED, R_SUCCESS, R_ADD, R_DEL, R_AUTH, R_NODE, R_IN_LOGS, + R_KEYS, R_TTY, R_NO_CONFIG, R_COMM, R_VIRT, R_INTEG }; + +static struct nv_pair optiontab[] = { + { R_AUTH, "-au" }, + { R_AUTH, "--auth" }, + { R_AVCS, "-a" }, + { R_AVCS, "--avc" }, + { R_ADD, "--add" }, + { R_CONFIGS, "-c" }, + { R_COMM, "--comm" }, + { R_CONFIGS, "--config" }, + { R_CRYPTO, "-cr" }, + { R_CRYPTO, "--crypto" }, + { R_DEL, "--delete" }, + { R_EVENTS, "-e" }, + { R_EVENTS, "--event" }, + { R_FILES, "-f" }, + { R_FILES, "--file" }, + { R_FAILED, "--failed" }, + { R_HOSTS, "-h" }, + { R_HOSTS, "--host" }, + { R_HELP, "--help" }, + { R_INTERPRET, "-i" }, + { R_INTERPRET, "--interpret" }, + { R_INFILE, "-if" }, + { R_INFILE, "--input" }, + { R_IN_LOGS, "--input-logs" }, + { R_INTEG, "--integrity" }, + { R_KEYS, "-k" }, + { R_KEYS, "--key" }, + { R_LOGINS, "-l" }, + { R_LOGINS, "--login" }, + { R_ACCT_MODS, "-m" }, + { R_ACCT_MODS, "--mods" }, + { R_MAC, "-ma" }, + { R_MAC, "--mac" }, + { R_NODE, "--node" }, + { R_NO_CONFIG, "-nc" }, + { R_NO_CONFIG, "--no-config" }, + { R_ANOMALY, "-n" }, + { R_ANOMALY, "--anomaly" }, + { R_PIDS, "-p" }, + { R_PIDS, "--pid" }, + { R_RESPONSE, "-r" }, + { R_RESPONSE, "--response" }, + { R_SYSCALLS, "-s" }, + { R_SYSCALLS, "--syscall" }, + { R_SUCCESS, "--success" }, + { R_SUMMARY_DET, "--summary" }, + { R_LOG_TIMES, "-t" }, + { R_LOG_TIMES, "--log" }, + { R_TIME_END, "-te"}, + { R_TIME_END, "--end"}, + { R_TERMINALS, "-tm"}, // don't like this + { R_TERMINALS, "--terminal"}, // don't like this + { R_TIME_START, "-ts" }, + { R_TTY, "--tty" }, + { R_TIME_START, "--start" }, + { R_USERS, "-u" }, + { R_USERS, "--user" }, + { R_VERSION, "-v" }, + { R_VERSION, "--version" }, + { R_EXES, "-x" }, + { R_EXES, "--executable" }, + { R_VIRT, "--virt" } +}; +#define OPTION_NAMES (sizeof(optiontab)/sizeof(optiontab[0])) + + +static int audit_lookup_option(const char *name) +{ + int i; + + for (i = 0; i < OPTION_NAMES; i++) + if (!strcmp(optiontab[i].name, name)) + return optiontab[i].value; + return -1; +} + +static void usage(void) +{ + printf("usage: aureport [options]\n" + "\t-a,--avc\t\t\tAvc report\n" + "\t-au,--auth\t\t\tAuthentication report\n" + "\t--comm\t\t\t\tCommands run report\n" + "\t-c,--config\t\t\tConfig change report\n" + "\t-cr,--crypto\t\t\tCrypto report\n" + "\t-e,--event\t\t\tEvent report\n" + "\t-f,--file\t\t\tFile name report\n" + "\t--failed\t\t\tonly failed events in report\n" + "\t-h,--host\t\t\tRemote Host name report\n" + "\t--help\t\t\t\thelp\n" + "\t-i,--interpret\t\t\tInterpretive mode\n" + "\t-if,--input \tuse this file as input\n" + "\t--input-logs\t\t\tUse the logs even if stdin is a pipe\n" + "\t--integrity\t\t\tIntegrity event report\n" + "\t-l,--login\t\t\tLogin report\n" + "\t-k,--key\t\t\tKey report\n" + "\t-m,--mods\t\t\tModification to accounts report\n" + "\t-ma,--mac\t\t\tMandatory Access Control (MAC) report\n" + "\t-n,--anomaly\t\t\taNomaly report\n" + "\t-nc,--no-config\t\t\tDon't include config events\n" + "\t--node \t\tOnly events from a specific node\n" + "\t-p,--pid\t\t\tPid report\n" + "\t-r,--response\t\t\tResponse to anomaly report\n" + "\t-s,--syscall\t\t\tSyscall report\n" + "\t--success\t\t\tonly success events in report\n" + "\t--summary\t\t\tsorted totals for main object in report\n" + "\t-t,--log\t\t\tLog time range report\n" + "\t-te,--end [end date] [end time]\tending date & time for reports\n" + "\t-tm,--terminal\t\t\tTerMinal name report\n" + "\t-ts,--start [start date] [start time]\tstarting data & time for reports\n" + "\t--tty\t\t\t\tReport about tty keystrokes\n" + "\t-u,--user\t\t\tUser name report\n" + "\t-v,--version\t\t\tVersion\n" + "\t--virt\t\t\t\tVirtualization report\n" + "\t-x,--executable\t\t\teXecutable name report\n" + "\tIf no report is given, the summary report will be displayed\n" + ); +} + +static int set_report(report_type_t r) +{ + if (report_type == RPT_UNSET) { + report_type = r; + return 0; + } else { + fprintf(stderr, "Error - only one report can be specified"); + return 1; + } +} + +static int set_detail(report_det_t d) +{ + if (report_detail == D_UNSET) { + report_detail = d; + return 0; + } else if (d == D_SUM) { + report_detail = d; + return 0; + } else { + return 1; + } +} + +/* + * This function examines the commandline parameters and sets various + * search options. It returns a 0 on success and < 0 on failure + */ +int check_params(int count, char *vars[]) +{ + int c = 1; + int retval = 0; + const char *optarg; + + while (c < count && retval == 0) { + // Go ahead and point to the next argument + if (c+1 < count) { + if (vars[c+1][0] != '-') + optarg = vars[c+1]; + else + optarg = NULL; + } else + optarg = NULL; + + switch (audit_lookup_option(vars[c])) { + case R_INFILE: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + user_file = strdup(optarg); + if (user_file == NULL) + retval = -1; + c++; + } + break; + case R_LOG_TIMES: + if (set_report(RPT_TIME)) + retval = -1; + else + set_detail(D_DETAILED); + break; + case R_AVCS: + if (set_report(RPT_AVC)) + retval = -1; + else { + set_detail(D_DETAILED); + event_comm = dummy; + event_subject = dummy; + event_object = dummy; + } + break; + case R_AUTH: + if (set_report(RPT_AUTH)) + retval = -1; + else { + set_detail(D_DETAILED); + event_exe = dummy; + event_hostname = dummy; + event_terminal = dummy; + event_uid = 1; + } + break; + case R_MAC: + if (set_report(RPT_MAC)) + retval = -1; + else { + set_detail(D_DETAILED); + event_loginuid = 1; + } + break; + case R_INTEG: + if (set_report(RPT_INTEG)) + retval = -1; + else { + set_detail(D_DETAILED); + event_loginuid = 1; + } + break; + case R_VIRT: + if (set_report(RPT_VIRT)) + retval = -1; + else { + set_detail(D_DETAILED); + } + break; + case R_CONFIGS: + if (set_report(RPT_CONFIG)) + retval = -1; + else { + set_detail(D_DETAILED); + event_loginuid = 1; + } + break; + case R_CRYPTO: + if (set_report(RPT_CRYPTO)) + retval = -1; + else { + set_detail(D_DETAILED); + event_loginuid = 1; + } + break; + case R_LOGINS: + if (set_report(RPT_LOGIN)) + retval = -1; + else { + set_detail(D_DETAILED); + event_exe = dummy; + event_hostname = dummy; + event_terminal = dummy; + event_loginuid = 1; + } + break; + case R_ACCT_MODS: + if (set_report(RPT_ACCT_MOD)) + retval = -1; + else { + set_detail(D_DETAILED); + event_exe = dummy; + event_hostname = dummy; + event_terminal = dummy; + event_loginuid = 1; + } + break; + case R_EVENTS: + if (set_report(RPT_EVENT)) + retval = -1; + else { +// if (!optarg) { + set_detail(D_DETAILED); + event_loginuid = 1; +// } else { +// UNIMPLEMENTED; +// set_detail(D_SPECIFIC); +// if (isdigit(optarg[0])) { +// errno = 0; +// event_id = strtoul(optarg, +// NULL, 10); +// if (errno) { +// fprintf(stderr, +// "Illegal value for audit event ID"); +// retval = -1; +// } +// c++; +// } else { +// fprintf(stderr, +// "Audit event id must be a numeric value, was %s\n", +// optarg); +// retval = -1; +// } +// } + } + break; + case R_FILES: + if (set_report(RPT_FILE)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_filename = dummy; + event_exe = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_HOSTS: + if (set_report(RPT_HOST)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_hostname = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_INTERPRET: + report_format = RPT_INTERP; + if (optarg) { + fprintf(stderr, + "Argument is NOT required for %s\n", + vars[c]); + retval = -1; + } + break; + case R_PIDS: + if (set_report(RPT_PID)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_exe = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_SYSCALLS: + if (set_report(RPT_SYSCALL)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_comm = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_TERMINALS: + if (set_report(RPT_TERM)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_terminal = dummy; + event_hostname = dummy; + event_exe = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_USERS: + if (set_report(RPT_USER)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_terminal = dummy; + event_hostname = dummy; + event_exe = dummy; + event_uid = 1; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_EXES: + if (set_report(RPT_EXE)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_terminal = dummy; + event_hostname = dummy; + event_exe = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_COMM: + if (set_report(RPT_COMM)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_terminal = dummy; + event_hostname = dummy; + event_comm = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_ANOMALY: + if (set_report(RPT_ANOMALY)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_terminal = dummy; + event_hostname = dummy; + event_exe = dummy; + event_comm = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_RESPONSE: + if (set_report(RPT_RESPONSE)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + } else { + UNIMPLEMENTED; + } + } + break; + case R_KEYS: + if (set_report(RPT_KEY)) + retval = -1; + else { + if (!optarg) { + set_detail(D_DETAILED); + event_exe = dummy; + event_key = dummy; + event_loginuid = 1; + } else { + UNIMPLEMENTED; + } + } + break; + case R_TTY: + if (set_report(RPT_TTY)) + retval = -1; + else { + set_detail(D_DETAILED); + event_session_id = 1; + event_loginuid = 1; + event_terminal = dummy; + event_comm = dummy; + } + break; + case R_TIME_END: + if (optarg) { + if ( (c+2 < count) && vars[c+2] && + (vars[c+2][0] != '-') ) { + /* Have both date and time - check order*/ + if (strchr(optarg, ':')) { + if (ausearch_time_end(vars[c+2], + optarg) != 0) + retval = -1; + } else { + if (ausearch_time_end(optarg, + vars[c+2]) != 0) + retval = -1; + } + c++; + } else { + // Check against recognized words + int t = lookup_time(optarg); + if (t >= 0) { + if (ausearch_time_end(optarg, + NULL) != 0) + retval = -1; + } else if ( (strchr(optarg, ':')) == NULL) { + /* Only have date */ + if (ausearch_time_end(optarg, + NULL) != 0) + retval = -1; + } else { + /* Only have time */ + if (ausearch_time_end(NULL, + optarg) != 0) + retval = -1; + } + } + c++; + break; + } + fprintf(stderr, + "%s requires either date and/or time\n", + vars[c]); + retval = -1; + break; + case R_TIME_START: + if (optarg) { + if ( (c+2 < count) && vars[c+2] && + (vars[c+2][0] != '-') ) { + /* Have both date and time - check order */ + if (strchr(optarg, ':')) { + if (ausearch_time_start( + vars[c+2], optarg) != 0) + retval = -1; + } else { + if (ausearch_time_start(optarg, + vars[c+2]) != 0) + retval = -1; + } + c++; + } else { + // Check against recognized words + int t = lookup_time(optarg); + if (t >= 0) { + if (ausearch_time_start(optarg, + "00:00:00") != 0) + retval = -1; + } else if ( strchr(optarg, ':') == NULL) { + /* Only have date */ + if (ausearch_time_start(optarg, + "00:00:00") != 0) + retval = -1; + } else { + /* Only have time */ + if (ausearch_time_start(NULL, + optarg) != 0) + retval = -1; + } + } + c++; + break; + } + fprintf(stderr, + "%s requires either date and/or time\n", + vars[c]); + retval = -1; + break; + case R_NODE: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + snode sn; + c++; + + if (!event_node_list) { + event_node_list = malloc(sizeof (slist)); + if (!event_node_list) { + retval = -1; + break; + } + slist_create(event_node_list); + } + + sn.str = strdup(optarg); + sn.key = NULL; + sn.hits=0; + slist_append(event_node_list, &sn); + } + break; + case R_SUMMARY_DET: + set_detail(D_SUM); + break; + case R_FAILED: + event_failed = F_FAILED; + break; + case R_SUCCESS: + event_failed = F_SUCCESS; + break; + case R_ADD: + event_conf_act = C_ADD; + break; + case R_DEL: + event_conf_act = C_DEL; + break; + case R_IN_LOGS: + force_logs = 1; + break; + case R_NO_CONFIG: + no_config = 1; + break; + case R_VERSION: + printf("aureport version %s\n", VERSION); + exit(0); + break; + case R_HELP: + usage(); + exit(0); + break; + default: + fprintf(stderr, "%s is an unsupported option\n", + vars[c]); + retval = -1; + break; + } + c++; + } + + if (retval >= 0) { + if (report_type == RPT_UNSET) { + if (set_report(RPT_SUMMARY)) + retval = -1; + else { + set_detail(D_SUM); + event_filename = dummy; + event_hostname = dummy; + event_terminal = dummy; + event_exe = dummy; + event_comm = dummy; + event_key = dummy; + event_loginuid = 1; + } + } + } else + usage(); + + return retval; +} + diff --git a/framework/src/audit/src/aureport-options.h b/framework/src/audit/src/aureport-options.h new file mode 100644 index 00000000..a559f645 --- /dev/null +++ b/framework/src/audit/src/aureport-options.h @@ -0,0 +1,55 @@ +/* aureport-options.h -- + * Copyright 2005-06, 2008,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUREPORT_OPTIONS_H +#define AUREPORT_OPTIONS_H + +#include +#include +#include "ausearch-common.h" + +/* Global variables that describe what search is to be performed */ +extern const char *event_context; + +typedef enum { RPT_UNSET, RPT_TIME, RPT_SUMMARY, RPT_AVC, RPT_MAC, + RPT_CONFIG, RPT_EVENT, RPT_FILE, RPT_HOST, RPT_LOGIN, + RPT_ACCT_MOD, RPT_PID, RPT_SYSCALL, RPT_TERM, RPT_USER, + RPT_EXE, RPT_ANOMALY, RPT_RESPONSE, RPT_CRYPTO, + RPT_AUTH, RPT_KEY, RPT_TTY, RPT_COMM, RPT_VIRT, + RPT_INTEG } report_type_t; + +typedef enum { D_UNSET, D_SUM, D_DETAILED, D_SPECIFIC } report_det_t; + +extern report_type_t report_type; +extern report_det_t report_detail; +extern report_t report_format; + + +/* Function to process commandline options */ +extern int check_params(int count, char *vars[]); + +#include +#define UNIMPLEMENTED { fprintf(stderr,"Unimplemented option\n"); exit(1); } + +#endif + diff --git a/framework/src/audit/src/aureport-output.c b/framework/src/audit/src/aureport-output.c new file mode 100644 index 00000000..9125d5ff --- /dev/null +++ b/framework/src/audit/src/aureport-output.c @@ -0,0 +1,1023 @@ +/* +* aureport-output.c - Print the report +* Copyright (c) 2005-06,2008,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include +#include "aureport-scan.h" +#include "aureport-options.h" +#include "ausearch-lookup.h" + +/* Locale functions */ +static void print_title_summary(void); +static void print_title_detailed(void); +static void do_summary_output(void); +static void do_file_summary_output(slist *sptr); +static void do_string_summary_output(slist *sptr); +static void do_user_summary_output(slist *sptr); +static void do_int_summary_output(ilist *sptr); +static void do_syscall_summary_output(ilist *sptr); +static void do_type_summary_output(ilist *sptr); + +/* Local Data */ +unsigned int line_item; + + +void print_title(void) +{ + line_item = 0U; + printf("\n"); + switch (report_detail) + { + case D_SUM: + print_title_summary(); + break; + case D_DETAILED: + print_title_detailed(); + break; + case D_SPECIFIC: + default: + break; + } +} + +static void print_title_summary(void) +{ + if (event_failed == F_FAILED) printf("Failed "); + if (event_failed == F_SUCCESS) printf("Success "); + switch (report_type) + { + case RPT_SUMMARY: + printf("Summary Report\n"); + printf("======================\n"); + break; + case RPT_AVC: + printf("Avc Object Summary Report\n"); + printf("=================================\n"); + printf("total obj\n"); + printf("=================================\n"); + break; + case RPT_MAC: + printf("MAC Summary Report\n"); + printf("==================\n"); + printf("total type\n"); + printf("==================\n"); + break; + case RPT_INTEG: + printf("Integrity Summary Report\n"); + printf("========================\n"); + printf("total type\n"); + printf("========================\n"); + break; + case RPT_VIRT: + printf("Virtualization Summary Report\n"); + printf("=============================\n"); + printf("total type\n"); + printf("=============================\n"); + break; + case RPT_CONFIG: + printf("Config Change Summary Report\n"); + printf("============================\n"); + printf("total type\n"); + printf("============================\n"); + break; + case RPT_AUTH: + printf("Authentication Summary Report\n"); + printf("=============================\n"); + printf("total acct\n"); + printf("=============================\n"); + break; + case RPT_LOGIN: + printf("Login Summary Report\n"); + printf("============================\n"); + printf("total auid\n"); + printf("============================\n"); + break; + case RPT_ACCT_MOD: + printf("Acct Modification Summary Report\n"); + printf("================================\n"); + printf("total type\n"); + printf("================================\n"); + break; + case RPT_TIME: + UNIMPLEMENTED; + break; + case RPT_EVENT: + printf("Event Summary Report\n"); + printf("======================\n"); + printf("total type\n"); + printf("======================\n"); + break; + case RPT_FILE: + printf("File Summary Report\n"); + printf("===========================\n"); + printf("total file\n"); + printf("===========================\n"); + break; + case RPT_HOST: + printf("Host Summary Report\n"); + printf("===========================\n"); + printf("total host\n"); + printf("===========================\n"); + break; + case RPT_PID: + printf("Pid Summary Report\n"); + printf("==========================\n"); + printf("total pid\n"); + printf("==========================\n"); + break; + case RPT_SYSCALL: + printf("Syscall Summary Report\n"); + printf("==========================\n"); + printf("total syscall\n"); + printf("==========================\n"); + break; + case RPT_TERM: + printf("Terminal Summary Report\n"); + printf("===============================\n"); + printf("total terminal\n"); + printf("===============================\n"); + break; + case RPT_USER: + printf("User Summary Report\n"); + printf("===========================\n"); + printf("total auid\n"); + printf("===========================\n"); + break; + case RPT_EXE: + printf("Executable Summary Report\n"); + printf("=================================\n"); + printf("total file\n"); + printf("=================================\n"); + break; + case RPT_COMM: + printf("Command Summary Report\n"); + printf("=================================\n"); + printf("total command\n"); + printf("=================================\n"); + break; + case RPT_ANOMALY: + printf("Anomaly Summary Report\n"); + printf("======================\n"); + printf("total type\n"); + printf("======================\n"); + break; + case RPT_RESPONSE: + printf("Anomaly Response Summary Report\n"); + printf("===============================\n"); + printf("total type\n"); + printf("===============================\n"); + break; + case RPT_CRYPTO: + printf("Crypto Summary Report\n"); + printf("=====================\n"); + printf("total type\n"); + printf("=====================\n"); + break; + case RPT_KEY: + printf("Key Summary Report\n"); + printf("===========================\n"); + printf("total key\n"); + printf("===========================\n"); + break; + case RPT_TTY: + UNIMPLEMENTED; + break; + default: + break; + } +} + +static void print_title_detailed(void) +{ + switch (report_type) + { + case RPT_AVC: + printf("AVC Report\n"); + printf( + "========================================================\n"); + printf( + "# date time comm subj syscall class permission obj event\n"); + printf( + "========================================================\n"); + break; + case RPT_CONFIG: + printf("Config Change Report\n"); + printf("===================================\n"); + printf("# date time type auid success event\n"); + printf("===================================\n"); + break; + case RPT_AUTH: + printf("Authentication Report\n"); + printf( + "============================================\n"); + printf( + "# date time acct host term exe success event\n"); + printf( + "============================================\n"); + break; + case RPT_LOGIN: + printf("Login Report\n"); + printf( + "============================================\n"); + printf( + "# date time auid host term exe success event\n"); + printf( + "============================================\n"); + break; + case RPT_ACCT_MOD: + printf("Account Modifications Report\n"); + printf( + "=================================================\n"); + printf( + "# date time auid addr term exe acct success event\n"); + printf( + "=================================================\n"); + break; + case RPT_TIME: + printf("Log Time Range Report\n"); + printf("=====================\n"); + break; + case RPT_EVENT: + if (report_detail == D_DETAILED) { + printf("Event Report\n"); + printf("===================================\n"); + printf("# date time event type auid success\n"); + printf("===================================\n"); + } else { + printf("Specific Event Report\n"); + printf("=====================\n"); + } + break; + case RPT_FILE: + if (report_detail == D_DETAILED) { + printf("File Report\n"); + printf( + "===============================================\n"); + printf( + "# date time file syscall success exe auid event\n"); + printf( + "===============================================\n"); + } else { + printf("Specific File Report\n"); + printf("====================\n"); + } + break; + case RPT_HOST: + if (report_detail == D_DETAILED) { + printf("Host Report\n"); + printf("===================================\n"); + printf("# date time host syscall auid event\n"); + printf("===================================\n"); + } else { + printf("Specific Host Report\n"); + printf("====================\n"); + } + break; + case RPT_PID: + if (report_detail == D_DETAILED) { + printf("Process ID Report\n"); + printf( + "======================================\n"); + printf( + "# date time pid exe syscall auid event\n"); + printf( + "======================================\n"); + } else { + printf("Specific Process ID Report\n"); + printf("==========================\n"); + } + break; + case RPT_SYSCALL: + if (report_detail == D_DETAILED) { + printf("Syscall Report\n"); + printf( + "=======================================\n"); + printf( + "# date time syscall pid comm auid event\n"); + printf( + "=======================================\n"); + } else { + printf("Specific Syscall Report\n"); + printf("=======================\n"); + } + break; + case RPT_TERM: + if (report_detail == D_DETAILED) { + printf("Terminal Report\n"); + printf( + "====================================\n"); + printf( + "# date time term host exe auid event\n"); + printf( + "====================================\n"); + } else { + printf("Specific Terminal Report\n"); + printf("========================\n"); + } + break; + case RPT_USER: + if (report_detail == D_DETAILED) { + printf("User ID Report\n"); + printf( + "====================================\n"); + printf( + "# date time auid term host exe event\n"); + printf( + "====================================\n"); + } else { + printf("Specific User ID Report\n"); + printf("=======================\n"); + } + break; + case RPT_EXE: + if (report_detail == D_DETAILED) { + printf("Executable Report\n"); + printf( + "====================================\n"); + printf( + "# date time exe term host auid event\n"); + printf( + "====================================\n"); + } else { + printf("Specific Executable Report\n"); + printf("==========================\n"); + } + break; + case RPT_COMM: + if (report_detail == D_DETAILED) { + printf("Command Report\n"); + printf( + "====================================\n"); + printf( + "# date time comm term host auid event\n"); + printf( + "=====================================\n"); + } else { + printf("Specific command Report\n"); + printf("=======================\n"); + } + break; + case RPT_ANOMALY: + if (report_detail == D_DETAILED) { + printf("Anomaly Report\n"); + printf( + "=========================================\n"); + printf( + "# date time type exe term host auid event\n"); + printf( + "=========================================\n"); + } else { + printf("Specific Anomaly Report\n"); + printf("=======================\n"); + } + break; + case RPT_RESPONSE: + if (report_detail == D_DETAILED) { + printf("Response to Anomaly Report\n"); + printf("==============================\n"); + printf("# date time type success event\n"); + printf("==============================\n"); + } else { + printf("Specific Response to Anomaly Report\n"); + printf("===================================\n"); + } + break; + case RPT_MAC: + if (report_detail == D_DETAILED) { + printf("MAC Report\n"); + printf("===================================\n"); + printf("# date time auid type success event\n"); + printf("===================================\n"); + } else { + printf("Specific Mandatory Access Control (MAC) Report\n"); + printf("===================================\n"); + } + break; + case RPT_INTEG: + if (report_detail == D_DETAILED) { + printf("Integrity Report\n"); + printf("==============================\n"); + printf("# date time type success event\n"); + printf("==============================\n"); + } else { + printf("Specific Integrity Report\n"); + printf("==============================\n"); + } + break; + case RPT_VIRT: + if (report_detail == D_DETAILED) { + printf("Virtualization Report\n"); + printf("==============================\n"); + printf("# date time type success event\n"); + printf("==============================\n"); + } else { + printf("Specific Virtualization Report\n"); + printf("==============================\n"); + } + break; + case RPT_CRYPTO: + if (report_detail == D_DETAILED) { + printf("Crypto Report\n"); + printf("===================================\n"); + printf("# date time auid type success event\n"); + printf("===================================\n"); + } else { + printf("Specific Crypto Report\n"); + printf("===================================\n"); + } + break; + case RPT_KEY: + if (report_detail == D_DETAILED) { + printf("Key Report\n"); + printf( + "===============================================\n"); + printf( + "# date time key success exe auid event\n"); + printf( + "===============================================\n"); + } else { + printf("Specific Key Report\n"); + printf("====================\n"); + } + break; + case RPT_TTY: + if (report_detail == D_DETAILED) { + printf("TTY Report\n"); + printf( + "===============================================\n"); + printf( + "# date time event auid term sess comm data\n"); + printf( + "===============================================\n"); + } else { + printf("Specific TTY Report\n"); + printf("====================\n"); + } + break; + default: + break; + } +} + +void print_per_event_item(llist *l) +{ + char buf[128]; + char name[64]; + char date[32]; + struct tm *tv; + + // The beginning is common to all reports + tv = localtime(&l->e.sec); + strftime(date, sizeof(date), "%x %T", tv); + if (report_type != RPT_AVC) { + line_item++; + printf("%u. %s ", line_item, date); + } + + switch (report_type) + { + case RPT_AVC: + alist_find_avc(l->s.avc); + do { + anode *an = l->s.avc->cur; + line_item++; + printf("%u. %s ", line_item, date); + // command subject syscall action obj res event + safe_print_string(l->s.comm ? l->s.comm : "?", 0); + printf(" %s %s %s %s %s %s %lu\n", + an->scontext, + aulookup_syscall(l, buf,sizeof(buf)), + an->avc_class, an->avc_perm, + an->tcontext, aulookup_result(an->avc_result), + l->e.serial); +//printf("items:%d\n", l->s.avc->cnt); + } while (alist_next_avc(l->s.avc)); + break; + case RPT_CONFIG: + // FIXME:who, action, what, outcome, event + // NOW: type auid success event + printf("%s %s %s %lu\n", + audit_msg_type_to_name(l->head->type), + aulookup_uid(l->s.loginuid, name, sizeof(name)), + aulookup_success(l->s.success), l->e.serial); + break; + case RPT_AUTH: + // who, addr, terminal, exe, success, event + // Special note...uid is used here because that is + // the way that the message works. This is because + // on failed logins, loginuid is not set. + safe_print_string(l->s.acct ? l->s.acct : + aulookup_uid(l->s.uid, name, sizeof(name)), 0); + printf(" %s %s %s %s %lu\n", + l->s.hostname, l->s.terminal, + l->s.exe, aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_LOGIN: + // who, addr, terminal, exe, success, event + // Special note...uid is used here because that is + // the way that the message works. This is because + // on failed logins, loginuid is not set. + safe_print_string(((l->s.success == S_FAILED) && + l->s.acct) ? l->s.acct : + aulookup_uid(l->s.uid, name, sizeof(name)), 0); + printf(" %s %s %s %s %lu\n", + l->s.hostname, l->s.terminal, + l->s.exe, aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_ACCT_MOD: + // who, addr, terminal, exe, success, event + safe_print_string( + aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %s %s %s %s %s %lu\n", + l->s.hostname ? l->s.hostname : "?", + l->s.terminal ? l->s.terminal : "?", + l->s.exe ? l->s.exe : "?", + l->s.acct ? l->s.acct : "?", + aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_EVENT: // report_detail == D_DETAILED + // event, type, who, success + printf("%lu %s ", + l->e.serial, + audit_msg_type_to_name(l->head->type)); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %s\n", aulookup_success(l->s.success)); + break; + case RPT_FILE: // report_detail == D_DETAILED + // file, syscall, success, exe, who, event + slist_first(l->s.filename); + safe_print_string(l->s.filename->cur->str,0); + printf(" %s %s ", + aulookup_syscall(l,buf,sizeof(buf)), + aulookup_success(l->s.success)); + safe_print_string(l->s.exe ? l->s.exe : "?", 0); + putchar(' '); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_HOST: // report_detail == D_DETAILED + // host, syscall, who, event + printf("%s %s ", + l->s.hostname, + aulookup_syscall(l,buf,sizeof(buf))); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_PID: // report_detail == D_DETAILED + // pid, exe, syscall, who, event + printf("%u ", l->s.pid); + safe_print_string(l->s.exe ? l->s.exe : "?", 0); + printf(" %s ", aulookup_syscall(l,buf,sizeof(buf))); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_SYSCALL: // report_detail == D_DETAILED + // syscall, pid, comm, who, event + printf("%s %u ", aulookup_syscall(l,buf,sizeof(buf)), + l->s.pid); + safe_print_string(l->s.comm ? l->s.comm : "?", 0); + putchar(' '); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_TERM: // report_detail == D_DETAILED + // terminal, host, exe, who, event + printf("%s %s ", + l->s.terminal, l->s.hostname); + safe_print_string(l->s.exe, 0); + putchar(' '); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_USER: // report_detail == D_DETAILED + // who, terminal, host, exe, event + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %s %s ", + l->s.terminal ? l->s.terminal : "?", + l->s.hostname ? l->s.hostname : "?"); + safe_print_string(l->s.exe ? l->s.exe : "?", 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_EXE: // report_detail == D_DETAILED + // exe, terminal, host, who, event + safe_print_string(l->s.exe ? l->s.exe : "?", 0); + printf(" %s %s ", + l->s.terminal ? l->s.terminal : "?", + l->s.hostname ? l->s.hostname : "?"); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_COMM: // report_detail == D_DETAILED + // comm, terminal, host, who, event + safe_print_string(l->s.comm ? l->s.comm : "?", 0); + printf(" %s %s ", + l->s.terminal ? l->s.terminal : "?", + l->s.hostname ? l->s.hostname : "?"); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_ANOMALY: // report_detail == D_DETAILED + // type exe term host auid event + printf("%s ", audit_msg_type_to_name(l->head->type)); + safe_print_string(l->s.exe ? l->s.exe : + l->s.comm ? l->s.comm: "?", 0); + printf(" %s %s ", + l->s.terminal ? l->s.terminal : "?", + l->s.hostname ? l->s.hostname : "?"); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_RESPONSE: // report_detail == D_DETAILED + // type success event + printf("%s %s %lu\n", + audit_msg_type_to_name(l->head->type), + aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_MAC: + // auid type success event + printf("%s %s %s %lu\n", + aulookup_uid(l->s.loginuid, name, sizeof(name)), + audit_msg_type_to_name(l->head->type), + aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_INTEG: + // type success event + printf("%s %s %lu\n", + audit_msg_type_to_name(l->head->type), + aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_VIRT: + // type success event + printf("%s %s %lu\n", + audit_msg_type_to_name(l->head->type), + aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_CRYPTO: + // auid type success event + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %s %s %lu\n", + audit_msg_type_to_name(l->head->type), + aulookup_success(l->s.success), + l->e.serial); + break; + case RPT_KEY: // report_detail == D_DETAILED + // key, success, exe, who, event + slist_first(l->s.key); + printf("%s %s ", l->s.key->cur->str, + aulookup_success(l->s.success)); + safe_print_string(l->s.exe ? l->s.exe : "?", 0); + putchar(' '); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %lu\n", l->e.serial); + break; + case RPT_TTY: { + char *ch, *ptr = strstr(l->head->message, "data="); + if (!ptr) + break; + ptr += 5; + ch = strrchr(ptr, ' '); + if (ch) + *ch = 0; + // event who term sess data + printf("%lu ", l->e.serial); + safe_print_string(aulookup_uid(l->s.loginuid, name, + sizeof(name)), 0); + printf(" %s %u ", + l->s.terminal ? l->s.terminal : "?", + l->s.session_id); + safe_print_string(l->s.comm ? l->s.comm: "?", 0); + putchar(' '); + print_tty_data(ptr); + printf("\n"); + } + break; + default: + break; + } +} + +void print_wrap_up(void) +{ + if (report_detail != D_SUM) + return; + + switch (report_type) + { + case RPT_SUMMARY: + do_summary_output(); + break; + case RPT_AVC: + slist_sort_by_hits(&sd.avc_objs); + do_string_summary_output(&sd.avc_objs); + break; + case RPT_CONFIG: /* We will borrow the pid list */ + ilist_sort_by_hits(&sd.pids); + do_type_summary_output(&sd.pids); + break; + case RPT_AUTH: + slist_sort_by_hits(&sd.users); + do_user_summary_output(&sd.users); + break; + case RPT_LOGIN: + slist_sort_by_hits(&sd.users); + do_user_summary_output(&sd.users); + break; + case RPT_ACCT_MOD: /* We will borrow the pid list */ + ilist_sort_by_hits(&sd.pids); + do_type_summary_output(&sd.pids); + break; + case RPT_EVENT: /* We will borrow the pid list */ + ilist_sort_by_hits(&sd.pids); + do_type_summary_output(&sd.pids); + break; + case RPT_FILE: + slist_sort_by_hits(&sd.files); + do_file_summary_output(&sd.files); + break; + case RPT_HOST: + slist_sort_by_hits(&sd.hosts); + do_string_summary_output(&sd.hosts); + break; + case RPT_PID: + ilist_sort_by_hits(&sd.pids); + do_int_summary_output(&sd.pids); + break; + case RPT_SYSCALL: + ilist_sort_by_hits(&sd.sys_list); + do_syscall_summary_output(&sd.sys_list); + break; + case RPT_TERM: + slist_sort_by_hits(&sd.terms); + do_string_summary_output(&sd.terms); + break; + case RPT_USER: + slist_sort_by_hits(&sd.users); + do_user_summary_output(&sd.users); + break; + case RPT_EXE: + slist_sort_by_hits(&sd.exes); + do_file_summary_output(&sd.exes); + break; + case RPT_COMM: + slist_sort_by_hits(&sd.comms); + do_file_summary_output(&sd.comms); + break; + case RPT_ANOMALY: + ilist_sort_by_hits(&sd.anom_list); + do_type_summary_output(&sd.anom_list); + break; + case RPT_RESPONSE: + ilist_sort_by_hits(&sd.resp_list); + do_type_summary_output(&sd.resp_list); + break; + case RPT_MAC: + ilist_sort_by_hits(&sd.mac_list); + do_type_summary_output(&sd.mac_list); + break; + case RPT_INTEG: + ilist_sort_by_hits(&sd.integ_list); + do_type_summary_output(&sd.integ_list); + break; + case RPT_VIRT: + ilist_sort_by_hits(&sd.virt_list); + do_type_summary_output(&sd.virt_list); + break; + case RPT_CRYPTO: + ilist_sort_by_hits(&sd.crypto_list); + do_type_summary_output(&sd.crypto_list); + break; + case RPT_KEY: + slist_sort_by_hits(&sd.keys); + do_file_summary_output(&sd.keys); + break; + default: + break; + } +} + +static void do_summary_output(void) +{ + extern event very_first_event; + extern event very_last_event; + + printf("Range of time in logs: "); + { + struct tm *btm; + char tmp[48]; + + btm = localtime(&very_first_event.sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + printf("%s.%03d - ", tmp, very_first_event.milli); + btm = localtime(&very_last_event.sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + printf("%s.%03d\n", tmp, very_last_event.milli); + } + printf("Selected time for report: "); + { + struct tm *btm; + char tmp[48]; + + if (start_time) + btm = localtime(&start_time); + else + btm = localtime(&very_first_event.sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + printf("%s - ", tmp); + if (end_time) + btm = localtime(&end_time); + else + btm = localtime(&very_last_event.sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + if (end_time) + printf("%s\n", tmp); + else + printf("%s.%03d\n", tmp, very_last_event.milli); + } + printf("Number of changes in configuration: %lu\n", sd.changes); + printf("Number of changes to accounts, groups, or roles: %lu\n", + sd.acct_changes); + printf("Number of logins: %lu\n", sd.good_logins); + printf("Number of failed logins: %lu\n", sd.bad_logins); + printf("Number of authentications: %lu\n", sd.good_auth); + printf("Number of failed authentications: %lu\n", sd.bad_auth); + printf("Number of users: %u\n", sd.users.cnt); + printf("Number of terminals: %u\n", sd.terms.cnt); + printf("Number of host names: %u\n", sd.hosts.cnt); + printf("Number of executables: %u\n", sd.exes.cnt); + printf("Number of commands: %u\n", sd.comms.cnt); + printf("Number of files: %u\n", sd.files.cnt); + printf("Number of AVC's: %lu\n", sd.avcs); + printf("Number of MAC events: %lu\n", sd.mac); + printf("Number of failed syscalls: %lu\n", sd.failed_syscalls); + printf("Number of anomaly events: %lu\n", sd.anomalies); + printf("Number of responses to anomaly events: %lu\n", sd.responses); + printf("Number of crypto events: %lu\n", sd.crypto); + printf("Number of integrity events: %lu\n", sd.integ); + printf("Number of virt events: %lu\n", sd.virt); + printf("Number of keys: %u\n", sd.keys.cnt); + printf("Number of process IDs: %u\n", sd.pids.cnt); + printf("Number of events: %lu\n", sd.events); + printf("\n"); +} + +static void do_file_summary_output(slist *sptr) +{ + const snode *sn; + + if (sptr->cnt == 0) { + printf("\n\n"); + return; + } + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + printf("%u ", sn->hits); + safe_print_string(sn->str, 1); + sn=slist_next(sptr); + } +} + +static void do_string_summary_output(slist *sptr) +{ + const snode *sn; + + if (sptr->cnt == 0) { + printf("\n\n"); + return; + } + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + printf("%u %s\n", sn->hits, sn->str); + sn=slist_next(sptr); + } +} + +static void do_user_summary_output(slist *sptr) +{ + const snode *sn; + + if (sptr->cnt == 0) { + printf("\n\n"); + return; + } + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + long uid; + char name[64]; + + if (sn->str[0] == '-' || isdigit(sn->str[0])) { + uid = strtol(sn->str, NULL, 10); + printf("%u ", sn->hits); + safe_print_string(aulookup_uid(uid, name, + sizeof(name)), 1); + } else { + printf("%u ", sn->hits); + safe_print_string(sn->str, 1); + } + sn=slist_next(sptr); + } +} + +static void do_int_summary_output(ilist *sptr) +{ + const int_node *in; + + if (sptr->cnt == 0) { + printf("\n\n"); + return; + } + ilist_first(sptr); + in=ilist_get_cur(sptr); + while (in) { + printf("%u %d\n", in->hits, in->num); + in=ilist_next(sptr); + } +} + +static void do_syscall_summary_output(ilist *sptr) +{ + const int_node *in; + + if (sptr->cnt == 0) { + printf("\n\n"); + return; + } + ilist_first(sptr); + in=ilist_get_cur(sptr); + while (in) { + const char *sys = NULL; + int machine = audit_elf_to_machine(in->aux1); + if (machine >= 0) + sys = audit_syscall_to_name(in->num, machine); + if (sys) + printf("%u %s\n", in->hits, sys); + else + printf("%u %d\n", in->hits, in->num); + in=ilist_next(sptr); + } +} + +static void do_type_summary_output(ilist *sptr) +{ + const int_node *in; + + if (sptr->cnt == 0) { + printf("\n\n"); + return; + } + ilist_first(sptr); + in=ilist_get_cur(sptr); + while (in) { + const char *name = audit_msg_type_to_name(in->num); + if (report_format == RPT_DEFAULT) + printf("%u %d\n", in->hits, in->num); + else + printf("%u %s\n", in->hits, name); + in=ilist_next(sptr); + } +} + diff --git a/framework/src/audit/src/aureport-scan.c b/framework/src/audit/src/aureport-scan.c new file mode 100644 index 00000000..6b2f5ee6 --- /dev/null +++ b/framework/src/audit/src/aureport-scan.c @@ -0,0 +1,974 @@ +/* +* aureport-scan.c - Extract interesting fields and check for match +* Copyright (c) 2005-06,2008,2011,2014-15 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include +#include "libaudit.h" +#include "aureport-options.h" +#include "ausearch-parse.h" +#include "ausearch-string.h" +#include "ausearch-lookup.h" +#include "aureport-scan.h" + +static void do_summary_total(llist *l); +static int per_event_summary(llist *l); +static int per_event_detailed(llist *l); + +summary_data sd; + +/* This function inits the counters */ +void reset_counters(void) +{ + sd.changes = 0UL; + sd.crypto = 0UL; + sd.acct_changes = 0UL; + sd.good_logins = 0UL; + sd.bad_logins = 0UL; + sd.good_auth = 0UL; + sd.bad_auth = 0UL; + sd.events = 0UL; + sd.avcs = 0UL; + sd.mac = 0UL; + sd.failed_syscalls = 0UL; + sd.anomalies = 0UL; + sd.responses = 0UL; + sd.virt = 0UL; + sd.integ = 0UL; + slist_create(&sd.users); + slist_create(&sd.terms); + slist_create(&sd.files); + slist_create(&sd.hosts); + slist_create(&sd.exes); + slist_create(&sd.comms); + slist_create(&sd.avc_objs); + slist_create(&sd.keys); + ilist_create(&sd.pids); + ilist_create(&sd.sys_list); + ilist_create(&sd.anom_list); + ilist_create(&sd.mac_list); + ilist_create(&sd.resp_list); + ilist_create(&sd.crypto_list); + ilist_create(&sd.virt_list); + ilist_create(&sd.integ_list); +} + +/* This function inits the counters */ +void destroy_counters(void) +{ + sd.changes = 0UL; + sd.crypto = 0UL; + sd.acct_changes = 0UL; + sd.good_logins = 0UL; + sd.bad_logins = 0UL; + sd.good_auth = 0UL; + sd.bad_auth = 0UL; + sd.events = 0UL; + sd.avcs = 0UL; + sd.mac = 0UL; + sd.failed_syscalls = 0UL; + sd.anomalies = 0UL; + sd.responses = 0UL; + sd.virt = 0UL; + sd.integ = 0UL; + slist_clear(&sd.users); + slist_clear(&sd.terms); + slist_clear(&sd.files); + slist_clear(&sd.hosts); + slist_clear(&sd.exes); + slist_clear(&sd.comms); + slist_clear(&sd.avc_objs); + slist_clear(&sd.keys); + ilist_clear(&sd.pids); + ilist_clear(&sd.sys_list); + ilist_clear(&sd.anom_list); + ilist_create(&sd.mac_list); + ilist_clear(&sd.resp_list); + ilist_create(&sd.crypto_list); + ilist_create(&sd.virt_list); + ilist_create(&sd.integ_list); +} + +/* This function will return 0 on no match and 1 on match */ +int classify_success(const llist *l) +{ +//printf("%d,succ=%d:%d\n", l->head->type, event_failed, l->s.success); + // If match only failed... + if (event_failed == F_FAILED) + return l->s.success == S_FAILED ? 1 : 0; + // If match only success... + if (event_failed == F_SUCCESS) + return l->s.success == S_SUCCESS ? 1 : 0; + // Otherwise...we don't care so pretend it matched + return 1; +} + +/* This function will return 0 on no match and 1 on match */ +int classify_conf(const llist *l) +{ + int rc = 1; + extern int no_config; + + switch (l->head->type) + { + case AUDIT_CONFIG_CHANGE: + if (no_config) + rc = 0; + break; + case AUDIT_USYS_CONFIG: + break; + case AUDIT_ADD_USER: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_DEL_USER: + if (event_conf_act == C_ADD) + rc = 0; + break; + case AUDIT_ADD_GROUP: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_DEL_GROUP: + if (event_conf_act == C_ADD) + rc = 0; + break; + case AUDIT_MAC_CIPSOV4_ADD: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_MAC_CIPSOV4_DEL: + if (event_conf_act == C_ADD) + rc = 0; + break; + case AUDIT_MAC_MAP_ADD: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_MAC_MAP_DEL: + if (event_conf_act == C_ADD) + rc = 0; + break; + case AUDIT_MAC_IPSEC_ADDSA: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_MAC_IPSEC_DELSA: + if (event_conf_act == C_ADD) + rc = 0; + break; + case AUDIT_MAC_IPSEC_ADDSPD: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_MAC_IPSEC_DELSPD: + if (event_conf_act == C_ADD) + rc = 0; + break; + case AUDIT_MAC_UNLBL_STCADD: + if (event_conf_act == C_DEL) + rc = 0; + break; + case AUDIT_MAC_UNLBL_STCDEL: + if (event_conf_act == C_ADD) + rc = 0; + break; + default: + break; + } +//printf("conf=%d:%d\n", l->head->type, rc); + return rc; +} + +/* + * This function performs that matching of search params with the record. + * It returns 1 on a match, and 0 if no match. + */ +int scan(llist *l) +{ + // Are we within time range? + if (start_time == 0 || l->e.sec >= start_time) { + if (end_time == 0 || l->e.sec <= end_time) { + // OK - do the heavier checking + int rc = extract_search_items(l); + if (rc == 0) { + if (event_node_list) { + const snode *sn; + int found=0; + slist *sptr = event_node_list; + + if (l->e.node == NULL) + return 0; + + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn && !found) { + if (sn->str && (!strcmp(sn->str, l->e.node))) + found++; + else + sn=slist_next(sptr); + } + + if (!found) + return 0; + } + if (classify_success(l) && classify_conf(l)) + return 1; + return 0; + } + } + } + return 0; +} + +int per_event_processing(llist *l) +{ + int rc; + + switch (report_detail) + { + case D_SUM: + rc = per_event_summary(l); + break; + case D_DETAILED: + rc = per_event_detailed(l); + break; + case D_SPECIFIC: + default: + rc = 0; + break; + } + return rc; +} + +static int per_event_summary(llist *l) +{ + int rc = 0; + + switch (report_type) + { + case RPT_SUMMARY: + do_summary_total(l); + rc = 1; + break; + case RPT_AVC: + if (list_find_msg(l, AUDIT_AVC)) { + if (alist_find_avc(l->s.avc)) { + do { + slist_add_if_uniq(&sd.avc_objs, + l->s.avc->cur->tcontext); + } while (alist_next_avc(l->s.avc)); + } + } else { + if (list_find_msg(l, AUDIT_USER_AVC)) { + if (alist_find_avc(l->s.avc)) { + do { + slist_add_if_uniq( + &sd.avc_objs, + l->s.avc->cur->tcontext); + } while (alist_next_avc( + l->s.avc)); + } + } + } + break; + case RPT_MAC: + if (list_find_msg_range(l, AUDIT_MAC_POLICY_LOAD, + AUDIT_MAC_MAP_DEL)) { + ilist_add_if_uniq(&sd.mac_list, + l->head->type, 0); + } else { + if (list_find_msg_range(l, + AUDIT_FIRST_USER_LSPP_MSG, + AUDIT_LAST_USER_LSPP_MSG)) { + ilist_add_if_uniq(&sd.mac_list, + l->head->type, 0); + } + } + break; + case RPT_INTEG: + if (list_find_msg_range(l, + AUDIT_INTEGRITY_FIRST_MSG, + AUDIT_INTEGRITY_LAST_MSG)) { + ilist_add_if_uniq(&sd.integ_list, + l->head->type, 0); + } + break; + case RPT_VIRT: + if (list_find_msg_range(l, + AUDIT_FIRST_VIRT_MSG, + AUDIT_LAST_VIRT_MSG)) { + ilist_add_if_uniq(&sd.virt_list, + l->head->type, 0); + } + break; + case RPT_CONFIG: /* We will borrow the pid list */ + if (list_find_msg(l, AUDIT_CONFIG_CHANGE) || + list_find_msg(l, AUDIT_DAEMON_CONFIG) || + list_find_msg(l, AUDIT_USYS_CONFIG) || + list_find_msg(l, AUDIT_NETFILTER_CFG) || + list_find_msg(l, AUDIT_FEATURE_CHANGE) || + list_find_msg(l, AUDIT_USER_MAC_CONFIG_CHANGE)|| + list_find_msg_range(l, + AUDIT_MAC_POLICY_LOAD, + AUDIT_MAC_UNLBL_STCDEL)) { + ilist_add_if_uniq(&sd.pids, l->head->type, 0); + } + break; + case RPT_AUTH: + if (list_find_msg(l, AUDIT_USER_AUTH)) { + if (l->s.loginuid == -2 && l->s.acct) + slist_add_if_uniq(&sd.users, l->s.acct); + else { + char name[64]; + + slist_add_if_uniq(&sd.users, + aulookup_uid(l->s.loginuid, + name, + sizeof(name)) + ); + } + } else if (list_find_msg(l, AUDIT_USER_MGMT)) { + // Only count the failures + if (l->s.success == S_FAILED) { + if (l->s.loginuid == -2 && + l->s.acct != NULL) + slist_add_if_uniq(&sd.users, l->s.acct); + else { + char name[64]; + + slist_add_if_uniq(&sd.users, + aulookup_uid( + l->s.loginuid, + name, + sizeof(name)) + ); + } + } + } + break; + case RPT_LOGIN: + if (list_find_msg(l, AUDIT_USER_LOGIN)) { + if ((int)l->s.loginuid < 0 && l->s.acct) + slist_add_if_uniq(&sd.users, l->s.acct); + else { + char name[64]; + + slist_add_if_uniq(&sd.users, + aulookup_uid(l->s.loginuid, + name, + sizeof(name)) + ); + } + } + break; + case RPT_ACCT_MOD: /* We will borrow the pid list */ + if (list_find_msg(l, AUDIT_USER_CHAUTHTOK) || + list_find_msg_range(l, + AUDIT_ADD_USER, AUDIT_DEL_GROUP) || + list_find_msg(l, AUDIT_USER_MGMT) || + list_find_msg(l, AUDIT_GRP_MGMT) || + list_find_msg_range(l, + AUDIT_ROLE_ASSIGN, + AUDIT_ROLE_REMOVE)) { + ilist_add_if_uniq(&sd.pids, l->head->type, 0); + } + break; + case RPT_EVENT: /* We will borrow the pid list */ + if (l->head->type != -1) { + ilist_add_if_uniq(&sd.pids, l->head->type, 0); + } + break; + case RPT_FILE: + if (l->s.filename) { + const snode *sn; + slist *sptr = l->s.filename; + + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + if (sn->str) + slist_add_if_uniq(&sd.files, + sn->str); + sn=slist_next(sptr); + } + } + break; + case RPT_HOST: + if (l->s.hostname) + slist_add_if_uniq(&sd.hosts, l->s.hostname); + break; + case RPT_PID: + if (l->s.pid != -1) { + ilist_add_if_uniq(&sd.pids, l->s.pid, 0); + } + break; + case RPT_SYSCALL: + if (l->s.syscall > 0) { + ilist_add_if_uniq(&sd.sys_list, + l->s.syscall, l->s.arch); + } + break; + case RPT_TERM: + if (l->s.terminal) + slist_add_if_uniq(&sd.terms, l->s.terminal); + break; + case RPT_USER: + if (l->s.loginuid != -2) { + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%d", l->s.loginuid); + slist_add_if_uniq(&sd.users, tmp); + } + break; + case RPT_EXE: + if (l->s.exe) + slist_add_if_uniq(&sd.exes, l->s.exe); + break; + case RPT_COMM: + if (l->s.comm) + slist_add_if_uniq(&sd.comms, l->s.comm); + break; + case RPT_ANOMALY: + if (list_find_msg_range(l, AUDIT_FIRST_ANOM_MSG, + AUDIT_LAST_ANOM_MSG)) { + ilist_add_if_uniq(&sd.anom_list, + l->head->type, 0); + } else { + if (list_find_msg_range(l, + AUDIT_FIRST_KERN_ANOM_MSG, + AUDIT_LAST_KERN_ANOM_MSG)) { + ilist_add_if_uniq(&sd.anom_list, + l->head->type, 0); + } + } + break; + case RPT_RESPONSE: + if (list_find_msg_range(l, AUDIT_FIRST_ANOM_RESP, + AUDIT_LAST_ANOM_RESP)) { + ilist_add_if_uniq(&sd.resp_list, + l->head->type, 0); + } + break; + case RPT_CRYPTO: + if (list_find_msg_range(l, AUDIT_FIRST_KERN_CRYPTO_MSG, + AUDIT_LAST_KERN_CRYPTO_MSG)) { + ilist_add_if_uniq(&sd.crypto_list, + l->head->type, 0); + } else { + if (list_find_msg_range(l, + AUDIT_FIRST_CRYPTO_MSG, + AUDIT_LAST_CRYPTO_MSG)) { + ilist_add_if_uniq(&sd.crypto_list, + l->head->type, 0); + } + } + break; + case RPT_KEY: + if (l->s.key) { + const snode *sn; + slist *sptr = l->s.key; + + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + if (sn->str && + strcmp(sn->str, "(null)")) + slist_add_if_uniq(&sd.keys, + sn->str); + sn=slist_next(sptr); + } + } + break; + case RPT_TTY: + UNIMPLEMENTED; + break; + default: + break; + } + return rc; +} + +static int per_event_detailed(llist *l) +{ + int rc = 0; + + switch (report_type) + { + case RPT_AVC: + if (list_find_msg(l, AUDIT_AVC)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_USER_AVC)) { + print_per_event_item(l); + rc = 1; + } + break; + case RPT_MAC: + if (report_detail == D_DETAILED) { + if (list_find_msg_range(l, + AUDIT_MAC_POLICY_LOAD, + AUDIT_MAC_UNLBL_STCDEL)) { + print_per_event_item(l); + rc = 1; + } else { + if (list_find_msg_range(l, + AUDIT_FIRST_USER_LSPP_MSG, + AUDIT_LAST_USER_LSPP_MSG)) { + print_per_event_item(l); + rc = 1; + } + } + } + break; + case RPT_INTEG: + if (report_detail == D_DETAILED) { + if (list_find_msg_range(l, + AUDIT_INTEGRITY_FIRST_MSG, + AUDIT_INTEGRITY_LAST_MSG)) { + print_per_event_item(l); + rc = 1; + } + } + break; + case RPT_VIRT: + if (report_detail == D_DETAILED) { + if (list_find_msg_range(l, + AUDIT_FIRST_VIRT_MSG, + AUDIT_LAST_VIRT_MSG)) { + print_per_event_item(l); + rc = 1; + } + } + break; + case RPT_CONFIG: + if (list_find_msg(l, AUDIT_CONFIG_CHANGE)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_DAEMON_CONFIG)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_USYS_CONFIG)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_NETFILTER_CFG)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_FEATURE_CHANGE)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, + AUDIT_USER_MAC_CONFIG_CHANGE)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg_range(l, + AUDIT_MAC_POLICY_LOAD, + AUDIT_MAC_UNLBL_STCDEL)) { + print_per_event_item(l); + rc = 1; + } + break; + case RPT_AUTH: + if (list_find_msg(l, AUDIT_USER_AUTH)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_USER_MGMT)) { + // Only count the failed acct + if (l->s.success == S_FAILED) { + print_per_event_item(l); + rc = 1; + } + } + break; + case RPT_LOGIN: + if (list_find_msg(l, AUDIT_USER_LOGIN)) { + print_per_event_item(l); + rc = 1; + } + break; + case RPT_ACCT_MOD: + if (list_find_msg(l, AUDIT_USER_CHAUTHTOK)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg_range(l, + AUDIT_ADD_USER, AUDIT_DEL_GROUP)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_USER_MGMT)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg(l, AUDIT_GRP_MGMT)) { + print_per_event_item(l); + rc = 1; + } else if (list_find_msg_range(l, + AUDIT_ROLE_ASSIGN, + AUDIT_ROLE_REMOVE)) { + print_per_event_item(l); + rc = 1; + } + break; + case RPT_EVENT: + list_first(l); + if (report_detail == D_DETAILED) { + print_per_event_item(l); + rc = 1; + } else { // specific event report + UNIMPLEMENTED; + } + break; + case RPT_FILE: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.filename) { + print_per_event_item(l); + rc = 1; + } + } else { // specific file report + UNIMPLEMENTED; + } + break; + case RPT_HOST: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.hostname) { + print_per_event_item(l); + rc = 1; + } + } else { // specific host report + UNIMPLEMENTED; + } + break; + case RPT_PID: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.pid >= 0) { + print_per_event_item(l); + rc = 1; + } + } else { // specific pid report + UNIMPLEMENTED; + } + break; + case RPT_SYSCALL: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.syscall) { + print_per_event_item(l); + rc = 1; + } + } else { // specific syscall report + UNIMPLEMENTED; + } + break; + case RPT_TERM: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.terminal) { + print_per_event_item(l); + rc = 1; + } + } else { // specific terminal report + UNIMPLEMENTED; + } + break; + case RPT_USER: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.uid != -1) { + print_per_event_item(l); + rc = 1; + } + } else { // specific user report + UNIMPLEMENTED; + } + break; + case RPT_EXE: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.exe) { + print_per_event_item(l); + rc = 1; + } + } else { // specific exe report + UNIMPLEMENTED; + } + break; + case RPT_COMM: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.comm) { + print_per_event_item(l); + rc = 1; + } + } else { // specific exe report + UNIMPLEMENTED; + } + break; + case RPT_ANOMALY: + if (report_detail == D_DETAILED) { + if (list_find_msg_range(l, + AUDIT_FIRST_ANOM_MSG, + AUDIT_LAST_ANOM_MSG)) { + print_per_event_item(l); + rc = 1; + } else { + if (list_find_msg_range(l, + AUDIT_FIRST_KERN_ANOM_MSG, + AUDIT_LAST_KERN_ANOM_MSG)) { + print_per_event_item(l); + rc = 1; + } + } + } else { // FIXME: specific anom report + UNIMPLEMENTED; + } + break; + case RPT_RESPONSE: + if (report_detail == D_DETAILED) { + if (list_find_msg_range(l, + AUDIT_FIRST_ANOM_RESP, + AUDIT_LAST_ANOM_RESP)) { + print_per_event_item(l); + rc = 1; + } + } else { // FIXME: specific resp report + UNIMPLEMENTED; + } + break; + case RPT_CRYPTO: + if (report_detail == D_DETAILED) { + if (list_find_msg_range(l, + AUDIT_FIRST_KERN_CRYPTO_MSG, + AUDIT_LAST_KERN_CRYPTO_MSG)) { + print_per_event_item(l); + rc = 1; + } else { + if (list_find_msg_range(l, + AUDIT_FIRST_CRYPTO_MSG, + AUDIT_LAST_CRYPTO_MSG)) { + print_per_event_item(l); + rc = 1; + } + } + } else { // FIXME: specific crypto report + UNIMPLEMENTED; + } + break; + case RPT_KEY: + list_first(l); + if (report_detail == D_DETAILED) { + if (l->s.key) { + slist_first(l->s.key); + if (strcmp(l->s.key->cur->str, + "(null)")) { + print_per_event_item(l); + rc = 1; + } + } + } else { // specific key report + UNIMPLEMENTED; + } + break; + case RPT_TTY: + if (l->head->type == AUDIT_TTY || + l->head->type == AUDIT_USER_TTY) { + print_per_event_item(l); + rc = 1; + } + break; + default: + break; + } + return rc; +} + +static void do_summary_total(llist *l) +{ + // add events + sd.events++; + + // add config changes + if (list_find_msg(l, AUDIT_CONFIG_CHANGE)) + sd.changes++; + if (list_find_msg(l, AUDIT_DAEMON_CONFIG)) + sd.changes++; + if (list_find_msg(l, AUDIT_USYS_CONFIG)) + sd.changes++; + if (list_find_msg(l, AUDIT_NETFILTER_CFG)) + sd.changes++; + if (list_find_msg(l, AUDIT_FEATURE_CHANGE)) + sd.changes++; + if (list_find_msg(l, AUDIT_USER_MAC_CONFIG_CHANGE)) + sd.changes++; + list_first(l); + if (list_find_msg_range(l, AUDIT_MAC_POLICY_LOAD, + AUDIT_MAC_UNLBL_STCDEL)) + sd.changes++; + + // add acct changes + if (list_find_msg(l, AUDIT_USER_CHAUTHTOK)) + sd.acct_changes++; + if (list_find_msg_range(l, AUDIT_ADD_USER, AUDIT_DEL_GROUP)) + sd.acct_changes++; + if (list_find_msg(l, AUDIT_USER_MGMT)) + sd.acct_changes++; + if (list_find_msg(l, AUDIT_GRP_MGMT)) + sd.acct_changes++; + list_first(l); + if (list_find_msg_range(l, AUDIT_ROLE_ASSIGN, AUDIT_ROLE_REMOVE)) + sd.acct_changes++; + + // Crypto + list_first(l); + if (list_find_msg_range(l, AUDIT_FIRST_KERN_CRYPTO_MSG, + AUDIT_LAST_KERN_CRYPTO_MSG)) + sd.crypto++; + if (list_find_msg_range(l, AUDIT_FIRST_CRYPTO_MSG, + AUDIT_LAST_CRYPTO_MSG)) + sd.crypto++; + + // add logins + if (list_find_msg(l, AUDIT_USER_LOGIN)) { + if (l->s.success == S_SUCCESS) + sd.good_logins++; + else if (l->s.success == S_FAILED) + sd.bad_logins++; + } + + // add use of auth + if (list_find_msg(l, AUDIT_USER_AUTH)) { + if (l->s.success == S_SUCCESS) + sd.good_auth++; + else if (l->s.success == S_FAILED) + sd.bad_auth++; + } else if (list_find_msg(l, AUDIT_USER_MGMT)) { + // Only count the failures + if (l->s.success == S_FAILED) + sd.bad_auth++; + } else if (list_find_msg(l, AUDIT_GRP_AUTH)) { + if (l->s.success == S_SUCCESS) + sd.good_auth++; + else if (l->s.success == S_FAILED) + sd.bad_auth++; + } + + // add users + if (l->s.loginuid != -2) { + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%d", l->s.loginuid); + slist_add_if_uniq(&sd.users, tmp); + } + + // add terminals + if (l->s.terminal) + slist_add_if_uniq(&sd.terms, l->s.terminal); + + // add hosts + if (l->s.hostname) + slist_add_if_uniq(&sd.hosts, l->s.hostname); + + // add execs + if (l->s.exe) + slist_add_if_uniq(&sd.exes, l->s.exe); + + // add comms + if (l->s.comm) + slist_add_if_uniq(&sd.comms, l->s.comm); + + // add files + if (l->s.filename) { + const snode *sn; + slist *sptr = l->s.filename; + + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + if (sn->str) + slist_add_if_uniq(&sd.files, sn->str); + sn=slist_next(sptr); + } + } + + // add avcs + if (list_find_msg(l, AUDIT_AVC)) + sd.avcs++; + else if (list_find_msg(l, AUDIT_USER_AVC)) + sd.avcs++; + + // MAC + list_first(l); + if (list_find_msg_range(l, AUDIT_MAC_POLICY_LOAD, + AUDIT_MAC_UNLBL_STCDEL)) + sd.mac++; + if (list_find_msg_range(l, AUDIT_FIRST_USER_LSPP_MSG, + AUDIT_LAST_USER_LSPP_MSG)) + sd.mac++; + + // Virt + list_first(l); + if (list_find_msg_range(l, AUDIT_FIRST_VIRT_MSG, + AUDIT_LAST_VIRT_MSG)) + sd.virt++; + + // Integrity + list_first(l); + if (list_find_msg_range(l, AUDIT_INTEGRITY_FIRST_MSG, + AUDIT_INTEGRITY_LAST_MSG)) + sd.integ++; + + // add failed syscalls + if (l->s.success == S_FAILED && l->s.syscall > 0) + sd.failed_syscalls++; + + // add pids + if (l->s.pid != -1) { + ilist_add_if_uniq(&sd.pids, l->s.pid, 0); + } + + // add anomalies + if (list_find_msg_range(l, AUDIT_FIRST_ANOM_MSG, AUDIT_LAST_ANOM_MSG)) + sd.anomalies++; + if (list_find_msg_range(l, AUDIT_FIRST_KERN_ANOM_MSG, + AUDIT_LAST_KERN_ANOM_MSG)) + sd.anomalies++; + + // add response to anomalies + if (list_find_msg_range(l, AUDIT_FIRST_ANOM_RESP, AUDIT_LAST_ANOM_RESP)) + sd.responses++; + + // add keys + if (l->s.key) { + const snode *sn; + slist *sptr = l->s.key; + + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn) { + if (sn->str && strcmp(sn->str, "(null)")) { + slist_add_if_uniq(&sd.keys, sn->str); + } + sn=slist_next(sptr); + } + } +} + diff --git a/framework/src/audit/src/aureport-scan.h b/framework/src/audit/src/aureport-scan.h new file mode 100644 index 00000000..5044d96d --- /dev/null +++ b/framework/src/audit/src/aureport-scan.h @@ -0,0 +1,76 @@ +/* aureport-scan.h -- + * Copyright 2005-06,2008,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUREPORT_SCAN_H +#define AUREPORT_SCAN_H + +#include "ausearch-llist.h" +#include "ausearch-int.h" + +typedef struct sdata { + slist users; + slist terms; + slist files; + slist hosts; + slist exes; + slist comms; + slist avc_objs; + slist keys; + ilist pids; + ilist sys_list; + ilist anom_list; + ilist resp_list; + ilist mac_list; + ilist crypto_list; + ilist virt_list; + ilist integ_list; + unsigned long changes; + unsigned long crypto; + unsigned long acct_changes; + unsigned long good_logins; + unsigned long bad_logins; + unsigned long good_auth; + unsigned long bad_auth; + unsigned long events; + unsigned long avcs; + unsigned long mac; + unsigned long failed_syscalls; + unsigned long anomalies; + unsigned long responses; + unsigned long virt; + unsigned long integ; +} summary_data; + +void reset_counters(void); +void destroy_counters(void); +int scan(llist *l); +int per_event_processing(llist *l); + +void print_title(void); +void print_per_event_item(llist *l); +void print_wrap_up(void); + +extern summary_data sd; + +#endif + diff --git a/framework/src/audit/src/aureport.c b/framework/src/audit/src/aureport.c new file mode 100644 index 00000000..98511e01 --- /dev/null +++ b/framework/src/audit/src/aureport.c @@ -0,0 +1,338 @@ +/* + * aureport.c - main file for aureport utility + * Copyright 2005-08, 2010,11,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "auditd-config.h" +#include "aureport-options.h" +#include "aureport-scan.h" +#include "ausearch-lol.h" +#include "ausearch-lookup.h" + + +event very_first_event, very_last_event; +static FILE *log_fd = NULL; +static lol lo; +static int found = 0; +static int files_to_process = 0; // Logs left when processing multiple +static int userfile_is_dir = 0; +static int process_logs(void); +static int process_log_fd(const char *filename); +static int process_stdin(void); +static int process_file(char *filename); +static int get_record(llist **); + +extern char *user_file; +extern int force_logs; + + +static int is_pipe(int fd) +{ + struct stat st; + + if (fstat(fd, &st) == 0) { + if (S_ISFIFO(st.st_mode)) + return 1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + struct rlimit limit; + int rc; + + /* Check params and build regexpr */ + setlocale (LC_ALL, ""); + if (check_params(argc, argv)) + return 1; + + /* Raise the rlimits in case we're being started from a shell + * with restrictions. Not a fatal error. */ + limit.rlim_cur = RLIM_INFINITY; + limit.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_FSIZE, &limit); + setrlimit(RLIMIT_CPU, &limit); + set_aumessage_mode(MSG_STDERR, DBG_NO); + (void) umask( umask( 077 ) | 027 ); + very_first_event.sec = 0; + reset_counters(); + + print_title(); + lol_create(&lo); + if (user_file) { + struct stat sb; + if (stat(user_file, &sb) == -1) { + perror("stat"); + return 1; + } else { + switch (sb.st_mode & S_IFMT) { + case S_IFDIR: + userfile_is_dir = 1; + rc = process_logs(); + break; + case S_IFREG: + default: + rc = process_file(user_file); + break; + } + } + } else if (force_logs) + rc = process_logs(); + else if (is_pipe(0)) + rc = process_stdin(); + else + rc = process_logs(); + lol_clear(&lo); + if (rc) + return rc; + + if (!found && report_detail == D_DETAILED && report_type != RPT_TIME) { + printf("\n\n"); + destroy_counters(); + aulookup_destroy_uid_list(); + aulookup_destroy_gid_list(); + return 1; + } else + print_wrap_up(); + destroy_counters(); + aulookup_destroy_uid_list(); + aulookup_destroy_gid_list(); + free(user_file); + return 0; +} + +static int process_logs(void) +{ + struct daemon_conf config; + char *filename; + int len, num = 0; + + if (user_file && userfile_is_dir) { + char dirname[MAXPATHLEN]; + clear_config (&config); + + strcpy(dirname, user_file); + if (dirname[strlen(dirname)-1] != '/') + strcat(dirname, "/"); + strcat (dirname, "audit.log"); + free((void *)config.log_file); + config.log_file=strdup(dirname); + fprintf(stderr, "NOTE - using logs in %s\n", config.log_file); + } else { + /* Load config so we know where logs are */ + if (load_config(&config, TEST_SEARCH)) + fprintf(stderr, "NOTE - using built-in logs: %s\n", + config.log_file); + } + + /* for each file */ + len = strlen(config.log_file) + 16; + filename = malloc(len); + if (!filename) { + fprintf(stderr, "No memory\n"); + free_config(&config); + return 1; + } + /* Find oldest log file */ + snprintf(filename, len, "%s", config.log_file); + do { + if (access(filename, R_OK) != 0) + break; +// FIXME: do a time check and put them on linked list for later + num++; + snprintf(filename, len, "%s.%d", config.log_file, num); + } while (1); + num--; + /* + * We note how many files we need to process + */ + files_to_process = num; + + /* Got it, now process logs from last to first */ + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else + snprintf(filename, len, "%s", config.log_file); + do { + int ret; + if ((ret = process_file(filename))) { + free(filename); + free_config(&config); + return ret; + } + + /* Get next log file */ + files_to_process--; /* one less file to process */ + num--; + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else if (num == 0) + snprintf(filename, len, "%s", config.log_file); + else + break; + } while (1); + free(filename); + free_config(&config); + return 0; +} + +static int process_log_fd(const char *filename) +{ + llist *entries; // entries in a record + int ret; + int first = 0; + event first_event, last_event; + + last_event.sec = 0; + last_event.milli = 0; + + /* For each record in file */ + do { + ret = get_record(&entries); + if ((ret != 0)||(entries->cnt == 0)) + break; + // If report is RPT_TIME or RPT_SUMMARY, get + if (report_type <= RPT_SUMMARY) { + if (first == 0) { + list_get_event(entries, &first_event); + first = 1; + if (very_first_event.sec == 0) + list_get_event(entries, + &very_first_event); + } + list_get_event(entries, &last_event); + } + if (scan(entries)) { + // This is the per entry action item + if (per_event_processing(entries)) + found = 1; + } + list_clear(entries); + free(entries); + } while (ret == 0); + fclose(log_fd); + // This is the per file action items + very_last_event.sec = last_event.sec; + very_last_event.milli = last_event.milli; + if (report_type == RPT_TIME) { + if (first == 0) { + printf("%s: no records\n", filename); + } else { + struct tm *btm; + char tmp[32]; + + printf("%s: ", filename); + btm = localtime(&first_event.sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + printf("%s.%03d - ", tmp, first_event.milli); + btm = localtime(&last_event.sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + printf("%s.%03d\n", tmp, last_event.milli); + } + } + + return 0; +} + +static int process_stdin(void) +{ + log_fd = stdin; + + return process_log_fd("stdin"); +} + +static int process_file(char *filename) +{ + log_fd = fopen(filename, "rm"); + if (log_fd == NULL) { + fprintf(stderr, "Error opening %s (%s)\n", filename, + strerror(errno)); + return 1; + } + + __fsetlocking(log_fd, FSETLOCKING_BYCALLER); + return process_log_fd(filename); +} + +/* + * This function returns a malloc'd buffer of the next record in the audit + * logs. It returns 0 on success, 1 on eof, -1 on error. + */ +static int get_record(llist **l) +{ + char *rc; + char *buff = NULL; + + *l = get_ready_event(&lo); + if (*l) + return 0; + + while (1) { + if (!buff) { + buff = malloc(MAX_AUDIT_MESSAGE_LENGTH); + if (!buff) + return -1; + } + rc = fgets_unlocked(buff, MAX_AUDIT_MESSAGE_LENGTH, + log_fd); + if (rc) { + if (lol_add_record(&lo, buff)) { + *l = get_ready_event(&lo); + if (*l) + break; + } + } else { + free(buff); + if (feof_unlocked(log_fd)) { + // Only mark all events complete if this is + // the last file. + if (files_to_process == 0) { + terminate_all_events(&lo); + } + *l = get_ready_event(&lo); + if (*l) + return 0; + else + return 1; + } else + return -1; + } + } + free(buff); + return 0; +} + diff --git a/framework/src/audit/src/ausearch-avc.c b/framework/src/audit/src/ausearch-avc.c new file mode 100644 index 00000000..2d3b3199 --- /dev/null +++ b/framework/src/audit/src/ausearch-avc.c @@ -0,0 +1,222 @@ +/* +* ausearch-avc.c - Minimal linked list library for avcs +* Copyright (c) 2006,2008,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include "ausearch-avc.h" + + +void alist_create(alist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +anode *alist_next(alist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +static void alist_last(alist *l) +{ + register anode* cur; + + if (l->head == NULL) + return; + + // Start with cur in hopes that we don't start at beginning + if (l->cur) + cur = l->cur; + else + cur = l->head; + + // Loop until no next value + while (cur->next) + cur = cur->next; + l->cur = cur; +} + +void alist_append(alist *l, anode *node) +{ + anode* newnode; + + newnode = malloc(sizeof(anode)); + + if (node->scontext) + newnode->scontext = node->scontext; + else + newnode->scontext = NULL; + + if (node->tcontext) + newnode->tcontext = node->tcontext; + else + newnode->tcontext = NULL; + + newnode->avc_result = node->avc_result; + + if (node->avc_perm) + newnode->avc_perm = node->avc_perm; + else + newnode->avc_perm = NULL; + + if (node->avc_class) + newnode->avc_class = node->avc_class; + else + newnode->avc_class = NULL; + + newnode->next = NULL; + + // Make sure cursor is at the end + alist_last(l); + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +int alist_find_subj(alist *l) +{ + register anode* window = l->head; + + while (window) { + if (window->scontext) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +anode *alist_next_subj(alist *l) +{ + if (l->cur == NULL) + return NULL; + while (l->cur->next) { + l->cur=l->cur->next; + if (l->cur->scontext) + return l->cur; + } + return NULL; +} + +int alist_find_obj(alist *l) +{ + register anode* window = l->head; + + while (window) { + if (window->tcontext) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +anode *alist_next_obj(alist *l) +{ + if (l->cur == NULL) + return NULL; + while (l->cur->next) { + l->cur=l->cur->next; + if (l->cur->tcontext) + return l->cur; + } + return NULL; +} + +int alist_find_avc(alist *l) +{ + register anode* window = l->head; + + while (window) { + if (window->avc_result != AVC_UNSET) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +anode *alist_next_avc(alist *l) +{ + if (l->cur == NULL) + return NULL; + while (l->cur->next) { + l->cur=l->cur->next; + if (l->cur->avc_result != AVC_UNSET) + return l->cur; + } + return NULL; +} + +void alist_clear(alist* l) +{ + anode* nextnode; + register anode* current; + + current = l->head; + while (current) { + nextnode=current->next; + anode_clear(current); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +void anode_init(anode *an) +{ + an->scontext = NULL; + an->tcontext = NULL; + an->avc_result = AVC_UNSET; + an->avc_perm = NULL; + an->avc_class = NULL; +} + +void anode_clear(anode *an) +{ + free(an->scontext); + free(an->tcontext); + free(an->avc_perm); + free(an->avc_class); +} + diff --git a/framework/src/audit/src/ausearch-avc.h b/framework/src/audit/src/ausearch-avc.h new file mode 100644 index 00000000..c31293e1 --- /dev/null +++ b/framework/src/audit/src/ausearch-avc.h @@ -0,0 +1,72 @@ +/* +* ausearch-avc.h - Header file for ausearch-string.c +* Copyright (c) 2006,2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AU_AVC_HEADER +#define AU_AVC_HEADER + +#include "config.h" +#include +#include "libaudit.h" + +typedef enum { AVC_UNSET, AVC_DENIED, AVC_GRANTED } avc_t; + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _anode{ + char *scontext; // se linux subject context + char *tcontext; // se linux object context + avc_t avc_result; // se linux avc denied/granted + char *avc_perm; // se linux avc permission mentioned + char *avc_class; // se linux class mentioned + struct _anode* next; // Next string node pointer +} anode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + anode *head; // List head + anode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} alist; + +void alist_create(alist *l); +static inline void alist_first(alist *l) { l->cur = l->head; } +anode *alist_next(alist *l); +static inline anode *alist_get_cur(alist *l) { return l->cur; } +void alist_append(alist *l, anode *node); +void anode_init(anode *an); +void anode_clear(anode *an); +void alist_clear(alist* l); + +/* See if any subj exists in list */ +int alist_find_subj(alist *l); +anode *alist_next_subj(alist *l); +/* See if any obj exists in list */ +int alist_find_obj(alist *l); +anode *alist_next_obj(alist *l); +/* See if any avc exists in list */ +int alist_find_avc(alist *l); +anode *alist_next_avc(alist *l); + +#endif + diff --git a/framework/src/audit/src/ausearch-checkpt.c b/framework/src/audit/src/ausearch-checkpt.c new file mode 100644 index 00000000..e0d0022c --- /dev/null +++ b/framework/src/audit/src/ausearch-checkpt.c @@ -0,0 +1,263 @@ +/* + * ausearch-checkpt.c - ausearch checkpointing feature + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include "ausearch-checkpt.h" + +#define DBG 0 /* set to non-zero for debug */ + +/* Remember why we failed */ +unsigned checkpt_failure = 0; + +/* + * Remember the file we were processing when we had incomplete events. + * We remember this via it's dev and inode + */ +static dev_t checkpt_dev = (dev_t)NULL; +static ino_t checkpt_ino = (ino_t)NULL; + +/* Remember the last event output */ +static event last_event = {0, 0, 0, NULL, 0}; + +/* Loaded values from a given checkpoint file */ +dev_t chkpt_input_dev = (dev_t)NULL; +ino_t chkpt_input_ino = (ino_t)NULL; +event chkpt_input_levent = {0, 0, 0, NULL, 0}; + +/* + * Record the dev_t and ino_t of the given file + * + * Returns: + * 1 Failed to get status + * 0 OK + */ +int set_ChkPtFileDetails(const char *fn) +{ + struct stat sbuf; + + if (stat(fn, &sbuf) != 0) { + fprintf(stderr, "Cannot stat audit file for checkpoint " + "details - %s: %s\n", fn, strerror(errno)); + checkpt_failure |= CP_STATFAILED; + return 1; + } + checkpt_dev = sbuf.st_dev; + checkpt_ino = sbuf.st_ino; + + return 0; +} + +/* + * Save the given event in the last_event record + * Returns: + * 1 no memory + * 0 OK + */ +int set_ChkPtLastEvent(const event *e) +{ + /* Set the event node if necessary */ + if (e->node) { + if (last_event.node) { + if (strcmp(e->node, last_event.node) != 0) { + free((void *)last_event.node); + last_event.node = strdup(e->node); + } + } else + last_event.node = strdup(e->node); + if (last_event.node == NULL) { + fprintf(stderr, "No memory to allocate " + "checkpoint last event node name\n"); + return 1; + } + } else { + if (last_event.node) + free((void *)last_event.node); + last_event.node = NULL; + } + last_event.sec = e->sec; + last_event.milli = e->milli; + last_event.serial = e->serial; + last_event.type = e->type; + + return 0; +} + +/* Free all checkpoint memory */ +void free_ChkPtMemory(void) +{ + if (last_event.node) + (void)free((void *)last_event.node); + last_event.node = NULL; + if (chkpt_input_levent.node) + (void)free((void *)chkpt_input_levent.node); + chkpt_input_levent.node = NULL; +} + +/* + * Save the checkpoint to the given file + * Returns: + * 1 io error + * 0 OK + */ +void save_ChkPt(const char *fn) +{ + FILE *fd; + + if ((fd = fopen(fn, "w")) == NULL) { + fprintf(stderr, "Cannot open checkpoint file - %s: %s\n", + fn, strerror(errno)); + checkpt_failure |= CP_STATUSIO; + return; + } + fprintf(fd, "dev=0x%X\ninode=0x%X\n", + (unsigned int)checkpt_dev, (unsigned int)checkpt_ino); + fprintf(fd, "output=%s %lu.%03u:%lu 0x%X\n", + last_event.node ? last_event.node : "-", + (long unsigned int)last_event.sec, last_event.milli, + last_event.serial, last_event.type); + fclose(fd); +} + +/* + * Parse a checkpoint file "output=" record + * Returns + * 1 failed to parse or no memory + * 0 parsed OK + */ +static int parse_checkpt_event(char *lbuf, int ndix, event *e) +{ + char *rest; + + /* + * Find the space after the node, then make it '\0' so + * we terminate the node value. We leave 'rest' at the start + * of the event time/serial element + */ + rest = strchr(&lbuf[ndix], ' '); + if (rest == NULL) { + fprintf(stderr, "Malformed output/event checkpoint line " + "near node - [%s]\n", lbuf); + checkpt_failure |= CP_STATUSBAD; + return 1; + } + *rest++ = '\0'; + + if (lbuf[ndix] == '-') + e->node = NULL; + else { + e->node = strdup(&lbuf[ndix]); + if (e->node == NULL) { + fprintf(stderr, "No memory for node when loading " + "checkpoint line - [%s]\n", lbuf); + checkpt_failure |= CP_NOMEM; + return 1; + } + } + if (sscanf(rest, "%lu.%03u:%lu 0x%X", &e->sec, &e->milli, + &e->serial, &e->type) != 4) { + fprintf(stderr, "Malformed output/event checkpoint line " + "after node - [%s]\n", lbuf); + checkpt_failure |= CP_STATUSBAD; + return 1; + } + + return 0; +} + +/* + * Load the checkpoint from the given file + * Returns: + * < -1 error + * == -1 no file present + * == 0 loaded data + */ +int load_ChkPt(const char *fn) +{ +#define MAX_LN 1023 + FILE *fd; + char lbuf[MAX_LN]; + + if ((fd = fopen(fn, "r")) == NULL) { + if (errno == ENOENT) + return -1; + fprintf(stderr, "Cannot open checkpoint file - %s: %s\n", + fn, strerror(errno)); + return -2; + } + while (fgets(lbuf, MAX_LN, fd) != NULL) { + size_t len = strlen(lbuf); + + if (len && lbuf[len - 1] == '\n') /* drop the newline */ + lbuf[len - 1] = '\0'; + + if (strncmp(lbuf, "dev=", 4) == 0) { + errno = 0; + chkpt_input_dev = strtoul(&lbuf[4], NULL, 16); + if (errno) { + fprintf(stderr, "Malformed dev checkpoint " + "line - [%s]\n", lbuf); + checkpt_failure |= CP_STATUSBAD; + break; + } + } else if (strncmp(lbuf, "inode=", 6) == 0) { + errno = 0; + chkpt_input_ino = strtoul(&lbuf[6], NULL, 16); + if (errno) { + fprintf(stderr, "Malformed inode checkpoint " + "line - [%s]\n", lbuf); + checkpt_failure |= CP_STATUSBAD; + break; + } + } else if (strncmp(lbuf, "output=", 7) == 0) { + if (parse_checkpt_event(lbuf, 7, &chkpt_input_levent)) + break; + } else { + fprintf(stderr, "Unknown checkpoint line - [%s]\n", + lbuf); + checkpt_failure |= CP_STATUSBAD; + break; + } + } + if ( (chkpt_input_ino == (ino_t)NULL) || + (chkpt_input_dev == (dev_t)NULL) ) { + fprintf(stderr, "Missing dev/inode lines from checkpoint " + "file %s\n", fn); + checkpt_failure |= CP_STATUSBAD; + } + fclose(fd); + + if (checkpt_failure) + return -3; + +#if DBG + { + fprintf(stderr, "Loaded %s - dev: 0x%X, ino: 0x%X\n", + fn, chkpt_input_dev, chkpt_input_ino); + fprintf(stderr, "output:%s %d.%03d:%lu 0x%X\n", + chkpt_input_levent.node ? chkpt_input_levent.node : "-", + chkpt_input_levent.sec, chkpt_input_levent.milli, + chkpt_input_levent.serial, chkpt_input_levent.type); + } +#endif /* DBG */ + return 0; +} + diff --git a/framework/src/audit/src/ausearch-checkpt.h b/framework/src/audit/src/ausearch-checkpt.h new file mode 100644 index 00000000..db66c254 --- /dev/null +++ b/framework/src/audit/src/ausearch-checkpt.h @@ -0,0 +1,42 @@ +/* + * ausearch-checkpt.h - ausearch checkpointing feature header file + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CHECKPT_HEADER +#define CHECKPT_HEADER + +#include +#include "ausearch-llist.h" + +int set_ChkPtFileDetails(const char *fn); +int set_ChkPtLastEvent(const event *e); +void free_ChkPtMemory(void); +void save_ChkPt(const char *fn); +int load_ChkPt(const char *fn); + +#define CP_NOMEM 0x0001 /* no memory when creating checkpoint list */ +#define CP_STATFAILED 0x0002 /* stat() call on last log file failed */ +#define CP_STATUSIO 0x0004 /* cannot open/read/write checkpoint file */ +#define CP_STATUSBAD 0x0008 /* malformed status checkpoint entries */ +#define CP_CORRUPTED 0x0010 /* corrupted times in checkpoint file */ + +extern unsigned checkpt_failure; + +extern dev_t chkpt_input_dev; +extern ino_t chkpt_input_ino; +extern event chkpt_input_levent; + +#endif /* CHECKPT_HEADER */ diff --git a/framework/src/audit/src/ausearch-common.h b/framework/src/audit/src/ausearch-common.h new file mode 100644 index 00000000..96b59c85 --- /dev/null +++ b/framework/src/audit/src/ausearch-common.h @@ -0,0 +1,73 @@ +/* ausearch-common.h -- + * Copyright 2006-08,2010,2014 Red Hat Inc., Durham, North Carolina. + * Copyright (c) 2011 IBM Corp. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * Marcelo Henrique Cerri + * + */ + +#ifndef AUREPORT_COMMON_H +#define AUREPORT_COMMON_H + +#include "ausearch-string.h" + +/* + * MAX_EVENT_DELTA_SECS is the maximum number of seconds it would take for + * auditd and the kernel to emit all of an events' records. Thus, when scanning + * a list of audit records without any End of Event marker, we can determine if + * all an event's records have been collected if we compare that event's time + * with the time of the event we are currently scanning. If + * MAX_EVENT_DELTA_SECS have passed, then the event is deamed to be complete + * and we have all it's records. + */ +#define MAX_EVENT_DELTA_SECS 2 + +/* Global variables that describe what search is to be performed */ +extern time_t start_time, end_time; +extern unsigned int event_id; +extern gid_t event_gid, event_egid; +extern pid_t event_pid; +extern int event_exact_match; +extern uid_t event_uid, event_euid, event_loginuid; +slist *event_node_list; +extern const char *event_comm; +extern const char *event_filename; +extern const char *event_hostname; +extern const char *event_terminal; +extern int event_syscall; +extern int event_machine; +extern const char *event_exe; +extern int event_ua, event_ga; +extern long long event_exit; +extern int event_exit_is_set; +extern const char *event_uuid; +extern const char *event_vmname; + +typedef enum { F_BOTH, F_FAILED, F_SUCCESS } failed_t; +typedef enum { C_NEITHER, C_ADD, C_DEL } conf_act_t; +typedef enum { S_UNSET=-1, S_FAILED, S_SUCCESS } success_t; +typedef enum { RPT_RAW, RPT_DEFAULT, RPT_INTERP, RPT_PRETTY } report_t; + +extern failed_t event_failed; +extern conf_act_t event_conf_act; +extern success_t event_success; + +#endif + diff --git a/framework/src/audit/src/ausearch-int.c b/framework/src/audit/src/ausearch-int.c new file mode 100644 index 00000000..a6bf8eb4 --- /dev/null +++ b/framework/src/audit/src/ausearch-int.c @@ -0,0 +1,162 @@ +/* +* ausearch-int.c - Minimal linked list library for integers +* Copyright (c) 2005,2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include "ausearch-int.h" + +void ilist_create(ilist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +int_node *ilist_next(ilist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +void ilist_append(ilist *l, int num, unsigned int hits, int aux) +{ + int_node* newnode; + + newnode = malloc(sizeof(int_node)); + + newnode->num = num; + newnode->hits = hits; + newnode->aux1 = aux; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +void ilist_clear(ilist* l) +{ + int_node* nextnode; + register int_node* current; + + if (l == NULL) + return; + + current = l->head; + while (current) { + nextnode=current->next; + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +int ilist_add_if_uniq(ilist *l, int num, int aux) +{ + register int_node *cur, *prev; + + prev = cur = l->head; + while (cur) { + if (cur->num == num) { + cur->hits++; + return 0; + } else if (num > cur->num) { + prev = cur; + cur = cur->next; + } else { + int head = 0; + + // Insert so list is from low to high + if (cur == l->head) { + l->head = NULL; + head = 1; + } else + l->cur = prev; + ilist_append(l, num, 1, aux); + if (head) + l->cur->next = prev; + else + l->cur->next = cur; + return 1; + } + } + + if (prev) + l->cur = prev; + + /* No matches, append to the end */ + ilist_append(l, num, 1, aux); + return 1; +} + +// If lprev would be NULL, use l->head +static void swap_nodes(int_node *lprev, int_node *left, int_node *right) +{ + int_node *t = right->next; + if (lprev) + lprev->next = right; + right->next = left; + left->next = t; +} + +// This will sort the list from most hits to least +void ilist_sort_by_hits(ilist *l) +{ + register int_node* cur, *prev; + + if (l->cnt <= 1) + return; + + prev = cur = l->head; + while (cur && cur->next) { + /* If the next node is bigger */ + if (cur->hits < cur->next->hits) { + if (cur == l->head) { + // Update the actual list head + l->head = cur->next; + prev = NULL; + } + swap_nodes(prev, cur, cur->next); + + // start over + prev = cur = l->head; + continue; + } + prev = cur; + cur = cur->next; + } + // End with cur pointing at first record + l->cur = l->head; +} + diff --git a/framework/src/audit/src/ausearch-int.h b/framework/src/audit/src/ausearch-int.h new file mode 100644 index 00000000..bc2c51fc --- /dev/null +++ b/framework/src/audit/src/ausearch-int.h @@ -0,0 +1,58 @@ +/* +* ausearch-int.h - Header file for ausearch-int.c +* Copyright (c) 2005,2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUINT_HEADER +#define AUINT_HEADER + +#include "config.h" + +/* This is the node of the linked list. Number & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _int_node{ + int num; // The number + unsigned int hits; // The number of times this was attempted to be added + int aux1; // Extra spot for data + struct _int_node* next; // Next string node pointer +} int_node; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + int_node *head; // List head + int_node *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} ilist; + +void ilist_create(ilist *l); +static inline void ilist_first(ilist *l) { l->cur = l->head; } +int_node *ilist_next(ilist *l); +static inline int_node *ilist_get_cur(ilist *l) { return l->cur; } +void ilist_append(ilist *l, int num, unsigned int hits, int aux); +void ilist_clear(ilist* l); + +/* append a number if its not already on the list */ +int ilist_add_if_uniq(ilist *l, int num, int aux); +void ilist_sort_by_hits(ilist *l); + +#endif + diff --git a/framework/src/audit/src/ausearch-llist.c b/framework/src/audit/src/ausearch-llist.c new file mode 100644 index 00000000..973941c5 --- /dev/null +++ b/framework/src/audit/src/ausearch-llist.c @@ -0,0 +1,257 @@ +/* +* ausearch-llist.c - Minimal linked list library +* Copyright (c) 2005-2008, 2011 Red Hat Inc., Durham, North Carolina. +* Copyright (c) 2011 IBM Corp. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +* Marcelo Henrique Cerri +*/ + +#include +#include +#include "ausearch-llist.h" + +void list_create(llist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; + l->e.milli = 0L; + l->e.sec = 0L; + l->e.serial = 0L; + l->e.node = NULL; + l->e.type = 0; + l->s.gid = -1; + l->s.egid = -1; + l->s.ppid = -1; + l->s.pid = -1; + l->s.success = S_UNSET; + l->s.uid = -1; + l->s.euid = -1; + l->s.loginuid = -2; + l->s.hostname = NULL; + l->s.filename = NULL; + l->s.terminal = NULL; + l->s.cwd = NULL; + l->s.exe = NULL; + l->s.key = NULL; + l->s.comm = NULL; + l->s.avc = NULL; + l->s.acct = NULL; + l->s.arch = 0; + l->s.syscall = 0; + l->s.session_id = -2; + l->s.uuid = NULL; + l->s.vmname = NULL; + l->s.exit = 0; + l->s.exit_is_set = 0; +} + +void list_last(llist *l) +{ + register lnode* window; + + if (l->head == NULL) + return; + + window = l->head; + while (window->next) + window = window->next; + l->cur = window; +} + +lnode *list_next(llist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +lnode *list_prev(llist *l) +{ + if (l->cur == NULL) + return NULL; + + if (l->cur->item == 0) + return NULL; + + list_find_item(l, l->cur->item-1); + return l->cur; +} + +void list_append(llist *l, lnode *node) +{ + lnode* newnode; + + newnode = malloc(sizeof(lnode)); + + if (node->message) + newnode->message = node->message; + else + newnode->message = NULL; + + newnode->mlen = node->mlen; + newnode->type = node->type; + newnode->a0 = node->a0; + newnode->a1 = node->a1; + newnode->item = l->cnt; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +int list_find_item(llist *l, unsigned int i) +{ + register lnode* window; + + if (l->cur && (l->cur->item <= i)) + window = l->cur; /* Try to use where we are */ + else + window = l->head; /* Can't, start over */ + + while (window) { + if (window->item == i) { + l->cur = window; + return 1; + } else + window = window->next; + } + return 0; +} + +void list_clear(llist* l) +{ + lnode* nextnode; + register lnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->message); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; + l->e.milli = 0L; + l->e.sec = 0L; + l->e.serial = 0L; + free((char *)l->e.node); + l->e.node = NULL; + l->e.type = 0; + l->s.gid = -1; + l->s.egid = -1; + l->s.ppid = -1; + l->s.pid = -1; + l->s.success = S_UNSET; + l->s.uid = -1; + l->s.euid = -1; + l->s.loginuid = -2; + free(l->s.hostname); + l->s.hostname = NULL; + if (l->s.filename) { + slist_clear(l->s.filename); + free(l->s.filename); + l->s.filename = NULL; + } + free(l->s.terminal); + l->s.terminal = NULL; + free(l->s.cwd); + l->s.cwd = NULL; + free(l->s.exe); + l->s.exe = NULL; + if (l->s.key) { + slist_clear(l->s.key); + free(l->s.key); + l->s.key = NULL; + } + free(l->s.comm); + l->s.comm = NULL; + if (l->s.avc) { + alist_clear(l->s.avc); + free(l->s.avc); + l->s.avc = NULL; + } + free(l->s.acct); + l->s.acct = NULL; + l->s.arch = 0; + l->s.syscall = 0; + l->s.session_id = -2; + free(l->s.uuid); + l->s.uuid = NULL; + free(l->s.vmname); + l->s.vmname = NULL; + l->s.exit = 0; + l->s.exit_is_set = 0; +} + +int list_get_event(llist* l, event *e) +{ + if (l == NULL || e == NULL) + return 0; + + e->sec = l->e.sec; + e->milli = l->e.milli; + e->serial = l->e.serial; + return 1; +} + +lnode *list_find_msg(llist *l, int i) +{ + register lnode* window; + + window = l->head; /* start at the beginning */ + while (window) { + if (window->type == i) { + l->cur = window; + return window; + } else + window = window->next; + } + return NULL; +} + +lnode *list_find_msg_range(llist *l, int low, int high) +{ + register lnode* window; + + if (high <= low) + return NULL; + + window = l->head; /* Start at the beginning */ + while (window) { + if (window->type >= low && window->type <= high) { + l->cur = window; + return window; + } else + window = window->next; + } + return NULL; +} + diff --git a/framework/src/audit/src/ausearch-llist.h b/framework/src/audit/src/ausearch-llist.h new file mode 100644 index 00000000..ada8ec81 --- /dev/null +++ b/framework/src/audit/src/ausearch-llist.h @@ -0,0 +1,117 @@ +/* +* ausearch-llist.h - Header file for ausearch-llist.c +* Copyright (c) 2005-2008, 2013-14 Red Hat Inc., Durham, North Carolina. +* Copyright (c) 2011 IBM Corp. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +* Marcelo Henrique Cerri +*/ + +#ifndef AULIST_HEADER +#define AULIST_HEADER + +#include "config.h" +#include +#include "ausearch-string.h" +#include "ausearch-avc.h" +#include "ausearch-common.h" + + +typedef struct +{ + time_t sec; // Event seconds + unsigned int milli; // millisecond of the timestamp + unsigned long serial; // Serial number of the event + const char *node; // Machine's node name + int type; // type of first event +} event; + +typedef struct +{ + pid_t ppid; // parent process ID + pid_t pid; // process ID + uid_t uid; // user ID + uid_t euid; // effective user ID + uid_t loginuid; // login user ID + gid_t gid; // group ID + gid_t egid; // effective group ID + success_t success; // success flag, 1 = yes, 0 = no, -1 = unset + int arch; // arch + int syscall; // syscall + uint32_t session_id; // Login session id + long long exit; // Syscall exit code + int exit_is_set; // Syscall exit code is valid + char *hostname; // remote hostname + slist *filename; // filename list + char *cwd; // current working dir + char *exe; // executable + slist *key; // key field + char *terminal; // terminal + char *comm; // comm name + alist *avc; // avcs for the event + char *acct; // account used when uid is invalid + char *uuid; // virtual machine unique universal identifier + char *vmname; // virtual machine name +} search_items; + +/* This is the node of the linked list. Any data elements that are per + * record goes here. */ +typedef struct _lnode{ + char *message; // The whole unparsed message + unsigned mlen; // Length of the message + int type; // message type (KERNEL, USER, LOGIN, etc) + unsigned long long a0; // argv 0 + unsigned long long a1; // argv 1 + unsigned int item; // Which item of the same event + struct _lnode* next; // Next node pointer +} lnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + lnode *head; // List head + lnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list + + // Data we add as 1 per event + event e; // event - time & serial number + search_items s; // items in master rec that are searchable +} llist; + +void list_create(llist *l); +static inline void list_first(llist *l) { l->cur = l->head; } +void list_last(llist *l); +lnode *list_next(llist *l); +lnode *list_prev(llist *l); +static inline lnode *list_get_cur(llist *l) { return l->cur; } +void list_append(llist *l, lnode *node); +void list_clear(llist* l); +int list_get_event(llist* l, event *e); + +/* Given a numeric index, find that record. */ +int list_find_item(llist *l, unsigned int i); + +/* Given a message type, find the matching node */ +lnode *list_find_msg(llist *l, int i); + +/* Given two message types, find the first matching node */ +lnode *list_find_msg_range(llist *l, int low, int high); + +#endif + diff --git a/framework/src/audit/src/ausearch-lol.c b/framework/src/audit/src/ausearch-lol.c new file mode 100644 index 00000000..48005126 --- /dev/null +++ b/framework/src/audit/src/ausearch-lol.c @@ -0,0 +1,296 @@ +/* +* ausearch-lol.c - linked list of linked lists library +* Copyright (c) 2008,2010,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "ausearch-lol.h" +#include +#include +#include +#include +#include "ausearch-common.h" +#include "private.h" + +#define ARRAY_LIMIT 80 +static int ready = 0; + +void lol_create(lol *lo) +{ + int size = ARRAY_LIMIT * sizeof(lolnode); + + lo->maxi = -1; + lo->limit = ARRAY_LIMIT; + lo->array = (lolnode *)malloc(size); + memset(lo->array, 0, size); +} + +void lol_clear(lol *lo) +{ + int i; + + for (i=0; i<=lo->maxi; i++) { + if (lo->array[i].status) { + list_clear(lo->array[i].l); + free(lo->array[i].l); + } + } + free(lo->array); + lo->array = NULL; + lo->maxi = -1; +} + +static void lol_append(lol *lo, llist *l) +{ + int i; + size_t new_size; + lolnode *ptr; + + for(i=0; ilimit; i++) { + lolnode *cur = &lo->array[i]; + if (cur->status == L_EMPTY) { + cur->l = l; + cur->status = L_BUILDING; + if (i > lo->maxi) + lo->maxi = i; + return; + } + } + // Overran the array...lets make it bigger + new_size = sizeof(lolnode) * (lo->limit + ARRAY_LIMIT); + ptr = realloc(lo->array, new_size); + if (ptr) { + lo->array = ptr; + memset(&lo->array[lo->limit], 0, sizeof(lolnode) * ARRAY_LIMIT); + lo->array[i].l = l; + lo->array[i].status = L_BUILDING; + lo->maxi = i; + lo->limit += ARRAY_LIMIT; + } +} + +static int str2event(char *s, event *e) +{ + char *ptr; + + errno = 0; + ptr = strchr(s+10, ':'); + if (ptr) { + e->serial = strtoul(ptr+1, NULL, 10); + *ptr = 0; + if (errno) + return -1; + } else + e->serial = 0; + ptr = strchr(s, '.'); + if (ptr) { + e->milli = strtoul(ptr+1, NULL, 10); + *ptr = 0; + if (errno) + return -1; + } else + e->milli = 0; + e->sec = strtoul(s, NULL, 10); + if (errno) + return -1; + return 0; +} + +static int inline events_are_equal(event *e1, event *e2) +{ + if (!(e1->serial == e2->serial && e1->milli == e2->milli && + e1->sec == e2->sec)) + return 0; + if (e1->node && e2->node) { + if (strcmp(e1->node, e2->node)) + return 0; + } else if (e1->node || e2->node) + return 0; + return 1; +} + +/* + * This function will look at the line and pick out pieces of it. + */ +static int extract_timestamp(const char *b, event *e) +{ + char *ptr, *tmp, *tnode, *ttype; + + e->node = NULL; + if (*b == 'n') + tmp = strndupa(b, 340); + else + tmp = strndupa(b, 80); + ptr = audit_strsplit(tmp); + if (ptr) { + // Check to see if this is the node info + if (*ptr == 'n') { + tnode = ptr+5; + ptr = audit_strsplit(NULL); + } else + tnode = NULL; + + // at this point we have type= + ttype = ptr+5; + + // Now should be pointing to msg= + ptr = audit_strsplit(NULL); + if (ptr) { + if (*(ptr+9) == '(') + ptr+=9; + else + ptr = strchr(ptr, '('); + if (ptr) { + // now we should be pointed at the timestamp + char *eptr; + ptr++; + eptr = strchr(ptr, ')'); + if (eptr) + *eptr = 0; + if (str2event(ptr, e)) { + fprintf(stderr, + "Error extracting time stamp (%s)\n", + ptr); + return 0; + } else if ((start_time && e->sec < start_time) + || (end_time && e->sec > end_time)) + return 0; + else { + if (tnode) + e->node = strdup(tnode); + e->type = audit_name_to_msg_type(ttype); + } + return 1; + } + // else we have a bad line + } + // else we have a bad line + } + // else we have a bad line + return 0; +} + +// This function will check events to see if they are complete +// FIXME: Can we think of other ways to determine if the event is done? +static void check_events(lol *lo, time_t sec) +{ + int i; + + for(i=0;i<=lo->maxi; i++) { + lolnode *cur = &lo->array[i]; + if (cur->status == L_BUILDING) { + // If 2 seconds have elapsed, we are done + if (cur->l->e.sec + 2 < sec) { + cur->status = L_COMPLETE; + ready++; + } else if (cur->l->e.type < AUDIT_FIRST_EVENT || + cur->l->e.type >= AUDIT_FIRST_ANOM_MSG) { + // If known to be 1 record event, we are done + cur->status = L_COMPLETE; + ready++; + } + } + } +} + +// This function adds a new record to an existing linked list +// or creates a new one if its a new event +int lol_add_record(lol *lo, char *buff) +{ + int i; + lnode n; + event e; + char *ptr; + llist *l; + + // Short circuit if event is not of interest + if (extract_timestamp(buff, &e) == 0) + return 0; + + ptr = strrchr(buff, 0x0a); + if (ptr) { + *ptr = 0; + n.mlen = ptr - buff; + } else + n.mlen = MAX_AUDIT_MESSAGE_LENGTH; + n.message=strdup(buff); + n.type = e.type; + + // Now see where this belongs + for (i=0; i<=lo->maxi; i++) { + if (lo->array[i].status == L_BUILDING) { + l = lo->array[i].l; + if (events_are_equal(&l->e, &e)) { + free((char *)e.node); + list_append(l, &n); + return 1; + } + } + } + // Create new event and fill it in + l = malloc(sizeof(llist)); + list_create(l); + l->e.milli = e.milli; + l->e.sec = e.sec; + l->e.serial = e.serial; + l->e.node = e.node; + l->e.type = e.type; + list_append(l, &n); + lol_append(lo, l); + check_events(lo, e.sec); + return 1; +} + +// This function will mark all events as "done" +void terminate_all_events(lol *lo) +{ + int i; + + for (i=0; i<=lo->maxi; i++) { + lolnode *cur = &lo->array[i]; + if (cur->status == L_BUILDING) { + cur->status = L_COMPLETE; + ready++; + } + } +//printf("maxi = %d\n",lo->maxi); +} + +/* Search the list for any event that is ready to go. The caller + * takes custody of the memory */ +llist* get_ready_event(lol *lo) +{ + int i; + + if (ready == 0) + return NULL; + + for (i=0; i<=lo->maxi; i++) { + lolnode *cur = &lo->array[i]; + if (cur->status == L_COMPLETE) { + cur->status = L_EMPTY; + ready--; + return cur->l; + } + } + + return NULL; +} + diff --git a/framework/src/audit/src/ausearch-lol.h b/framework/src/audit/src/ausearch-lol.h new file mode 100644 index 00000000..36b6bbca --- /dev/null +++ b/framework/src/audit/src/ausearch-lol.h @@ -0,0 +1,54 @@ +/* +* ausearch-lol.h - linked list of linked lists library header +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUSEARCH_LOL_HEADER +#define AUSEARCH_LOL_HEADER + +#include "config.h" +#include "ausearch-llist.h" + +typedef enum { L_EMPTY, L_BUILDING, L_COMPLETE } lol_t; + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _lolnode{ + llist *l; // The linked list + int status; // 0 = empty, 1 in use, 2 complete +} lolnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + lolnode *array; + int maxi; // Largest index used + int limit; // Number of nodes in the array +} lol; + +void lol_create(lol *lo); +void lol_clear(lol *lo); +int lol_add_record(lol *lo, char *buff); +void terminate_all_events(lol *lo); +llist* get_ready_event(lol *lo); + +#endif + diff --git a/framework/src/audit/src/ausearch-lookup.c b/framework/src/audit/src/ausearch-lookup.c new file mode 100644 index 00000000..10a219a5 --- /dev/null +++ b/framework/src/audit/src/ausearch-lookup.c @@ -0,0 +1,500 @@ +/* +* ausearch-lookup.c - Lookup values to something more readable +* Copyright (c) 2005-06,2011-12,2015 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include +#include +#include +#include "ausearch-lookup.h" +#include "ausearch-options.h" +#include "ausearch-nvpair.h" + +/* This is the name/value pair used by search tables */ +struct nv_pair { + int value; + const char *name; +}; + + +/* The machine based on elf type */ +static int machine = 0; +static const char *Q = "?"; +static const char *results[3]= { "unset", "denied", "granted" }; +static const char *success[3]= { "unset", "no", "yes" }; +static const char *aulookup_socketcall(long sc); +static const char *aulookup_ipccall(long ic); + +const char *aulookup_result(avc_t result) +{ + return results[result]; +} + +const char *aulookup_success(int s) +{ + switch (s) + { + default: + return success[0]; + break; + case S_FAILED: + return success[1]; + break; + case S_SUCCESS: + return success[2]; + break; + } +} + +const char *aulookup_syscall(llist *l, char *buf, size_t size) +{ + const char *sys; + + if (report_format <= RPT_DEFAULT) { + snprintf(buf, size, "%d", l->s.syscall); + return buf; + } + machine = audit_elf_to_machine(l->s.arch); + if (machine < 0) + return Q; + sys = audit_syscall_to_name(l->s.syscall, machine); + if (sys) { + const char *func = NULL; + if (strcmp(sys, "socketcall") == 0) { + if (list_find_item(l, AUDIT_SYSCALL)) + func = aulookup_socketcall((long)l->cur->a0); + } else if (strcmp(sys, "ipc") == 0) { + if(list_find_item(l, AUDIT_SYSCALL)) + func = aulookup_ipccall((long)l->cur->a0); + } + if (func) { + snprintf(buf, size, "%s(%s)", sys, func); + return buf; + } + return sys; + } + snprintf(buf, size, "%d", l->s.syscall); + return buf; +} + +// See include/linux/net.h +static struct nv_pair socktab[] = { + {SYS_SOCKET, "socket"}, + {SYS_BIND, "bind"}, + {SYS_CONNECT, "connect"}, + {SYS_LISTEN, "listen"}, + {SYS_ACCEPT, "accept"}, + {SYS_GETSOCKNAME, "getsockname"}, + {SYS_GETPEERNAME, "getpeername"}, + {SYS_SOCKETPAIR, "socketpair"}, + {SYS_SEND, "send"}, + {SYS_RECV, "recv"}, + {SYS_SENDTO, "sendto"}, + {SYS_RECVFROM, "recvfrom"}, + {SYS_SHUTDOWN, "shutdown"}, + {SYS_SETSOCKOPT, "setsockopt"}, + {SYS_GETSOCKOPT, "getsockopt"}, + {SYS_SENDMSG, "sendmsg"}, + {SYS_RECVMSG, "recvmsg"}, + {SYS_ACCEPT4, "accept4"}, + {19, "recvmmsg"}, + {20, "sendmmsg"} +}; +#define SOCK_NAMES (sizeof(socktab)/sizeof(socktab[0])) + +static const char *aulookup_socketcall(long sc) +{ + int i; + + for (i = 0; i < SOCK_NAMES; i++) + if (socktab[i].value == sc) + return socktab[i].name; + + return NULL; +} + +/* This is from asm/ipc.h. Copying it for now as some platforms + * have broken headers. */ +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define SEMTIMEDOP 4 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 + +/* + * This table maps ipc calls to their text name + */ +static struct nv_pair ipctab[] = { + {SEMOP, "semop"}, + {SEMGET, "semget"}, + {SEMCTL, "semctl"}, + {SEMTIMEDOP, "semtimedop"}, + {MSGSND, "msgsnd"}, + {MSGRCV, "msgrcv"}, + {MSGGET, "msgget"}, + {MSGCTL, "msgctl"}, + {SHMAT, "shmat"}, + {SHMDT, "shmdt"}, + {SHMGET, "shmget"}, + {SHMCTL, "shmctl"} +}; +#define IPC_NAMES (sizeof(ipctab)/sizeof(ipctab[0])) + +static const char *aulookup_ipccall(long ic) +{ + int i; + + for (i = 0; i < IPC_NAMES; i++) + if (ipctab[i].value == ic) + return ipctab[i].name; + + return NULL; +} + +static nvlist uid_nvl; +static int uid_list_created=0; +const char *aulookup_uid(uid_t uid, char *buf, size_t size) +{ + char *name = NULL; + int rc; + + if (report_format <= RPT_DEFAULT) { + snprintf(buf, size, "%d", uid); + return buf; + } + if (uid == -1) { + snprintf(buf, size, "unset"); + return buf; + } + + // Check the cache first + if (uid_list_created == 0) { + nvlist_create(&uid_nvl); + nvlist_clear(&uid_nvl); + uid_list_created = 1; + } + rc = nvlist_find_val(&uid_nvl, uid); + if (rc) { + name = uid_nvl.cur->name; + } else { + // Add it to cache + struct passwd *pw; + pw = getpwuid(uid); + if (pw) { + nvnode nv; + nv.name = strdup(pw->pw_name); + nv.val = uid; + nvlist_append(&uid_nvl, &nv); + name = uid_nvl.cur->name; + } + } + if (name != NULL) + snprintf(buf, size, "%s", name); + else + snprintf(buf, size, "unknown(%d)", uid); + return buf; +} + +void aulookup_destroy_uid_list(void) +{ + if (uid_list_created == 0) + return; + + nvlist_clear(&uid_nvl); + uid_list_created = 0; +} + +static nvlist gid_nvl; +static int gid_list_created=0; +const char *aulookup_gid(gid_t gid, char *buf, size_t size) +{ + char *name = NULL; + int rc; + + if (report_format <= RPT_DEFAULT) { + snprintf(buf, size, "%d", gid); + return buf; + } + if (gid == -1) { + snprintf(buf, size, "unset"); + return buf; + } + + // Check the cache first + if (gid_list_created == 0) { + nvlist_create(&gid_nvl); + nvlist_clear(&gid_nvl); + gid_list_created = 1; + } + rc = nvlist_find_val(&gid_nvl, gid); + if (rc) { + name = gid_nvl.cur->name; + } else { + // Add it to cache + struct group *gr; + gr = getgrgid(gid); + if (gr) { + nvnode nv; + nv.name = strdup(gr->gr_name); + nv.val = gid; + nvlist_append(&gid_nvl, &nv); + name = gid_nvl.cur->name; + } + } + if (name != NULL) + snprintf(buf, size, "%s", name); + else + snprintf(buf, size, "unknown(%d)", gid); + return buf; +} + +void aulookup_destroy_gid_list(void) +{ + if (gid_list_created == 0) + return; + + nvlist_clear(&gid_nvl); + gid_list_created = 0; +} + +int is_hex_string(const char *str) +{ + int c=0; + while (*str) { + if (!isxdigit(*str)) + return 0; + str++; + c++; + } + return 1; +} +/* + * This function will take a pointer to a 2 byte Ascii character buffer and + * return the actual hex value. + */ +static unsigned char x2c(unsigned char *buf) +{ + static const char AsciiArray[17] = "0123456789ABCDEF"; + char *ptr; + unsigned char total=0; + + ptr = strchr(AsciiArray, (char)toupper(buf[0])); + if (ptr) + total = (unsigned char)(((ptr-AsciiArray) & 0x0F)<<4); + ptr = strchr(AsciiArray, (char)toupper(buf[1])); + if (ptr) + total += (unsigned char)((ptr-AsciiArray) & 0x0F); + + return total; +} + +/* returns a freshly malloc'ed and converted buffer */ +char *unescape(const char *buf) +{ + int len, i; + char *str, *strptr; + const char *ptr = buf; + + /* Find the end of the name */ + if (*ptr == '(') { + ptr = strchr(ptr, ')'); + if (ptr == NULL) + return NULL; + else + ptr++; + } else { + while (isxdigit(*ptr)) + ptr++; + } + str = strndup(buf, ptr - buf); + + if (*buf == '(') + return str; + + /* We can get away with this since the buffer is 2 times + * bigger than what we are putting there. + */ + len = strlen(str); + if (len < 2) { + free(str); + return NULL; + } + strptr = str; + for (i=0; i> 6)); + putchar('0' + ((s[i] & 0070) >> 3)); + putchar('0' + (s[i] & 0007)); + } else + putchar(s[i]); + i++; + } +} + +void safe_print_string_n(const char *s, unsigned int len, int ret) +{ + if (need_sanitize(s, len)) { + sanitize(s, len); + if (ret) + putchar('\n'); + } else if (ret) + puts(s); + else + printf("%s", s); +} + +void safe_print_string(const char *s, int ret) +{ + safe_print_string_n(s, strlen(s), ret); +} + +/* Represent c as a character within a quoted string, and append it to buf. */ +static void tty_printable_char(unsigned char c) +{ + if (c < 0x20 || c > 0x7E) { + putchar('\\'); + putchar('0' + ((c >> 6) & 07)); + putchar('0' + ((c >> 3) & 07)); + putchar('0' + (c & 07)); + } else { + if (c == '\\' || c == '"') + putchar('\\'); + putchar(c); + } +} + +/* Search for a name of a sequence of TTY bytes. + * If found, return the name and advance *INPUT. + * Return NULL otherwise. + */ +static const char *tty_find_named_key(unsigned char **input, size_t input_len) +{ + /* NUL-terminated list of (sequence, NUL, name, NUL) entries. + First match wins, even if a longer match were possible later */ + static const unsigned char named_keys[] = +#define E(SEQ, NAME) SEQ "\0" NAME "\0" +#include "auparse/tty_named_keys.h" +#undef E + "\0"; + + unsigned char *src; + const unsigned char *nk; + + src = *input; + if (*src >= ' ' && (*src < 0x7F || *src >= 0xA0)) + return NULL; /* Fast path */ + nk = named_keys; + do { + const unsigned char *p; + size_t nk_len; + + p = strchr((const char *)nk, '\0'); + nk_len = p - nk; + if (nk_len <= input_len && memcmp(src, nk, nk_len) == 0) { + *input += nk_len; + return (const char *)(p + 1); + } + nk = strchr((const char *)p + 1, '\0') + 1; + } while (*nk != '\0'); + return NULL; +} + +void print_tty_data(const char *val) +{ + int need_comma, in_printable = 0; + unsigned char *data, *data_pos, *data_end; + + if (!is_hex_string(val)) { + printf("%s", val); + return; + } + + if ((data = unescape((char *)val)) == NULL) { + printf("conversion error(%s)", val); + return; + } + + data_end = data + strlen(val) / 2; + data_pos = data; + need_comma = 0; + while (data_pos < data_end) { + /* FIXME: Unicode */ + const char *desc; + + desc = tty_find_named_key(&data_pos, data_end - data_pos); + if (desc != NULL) { + if (in_printable != 0) { + putchar('"'); + in_printable = 0; + } + if (need_comma != 0) + putchar(','); + printf("<%s>", desc); + } else { + if (in_printable == 0) { + if (need_comma != 0) + putchar(','); + putchar('"'); + in_printable = 1; + } + tty_printable_char(*data_pos); + data_pos++; + } + need_comma = 1; + } + if (in_printable != 0) + putchar('"'); + free(data); +} + diff --git a/framework/src/audit/src/ausearch-lookup.h b/framework/src/audit/src/ausearch-lookup.h new file mode 100644 index 00000000..ab12e176 --- /dev/null +++ b/framework/src/audit/src/ausearch-lookup.h @@ -0,0 +1,50 @@ +/* +* ausearch-lookup.h - Header file for ausearch-lookup.c +* Copyright (c) 2005-06,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AULOOKUP_HEADER +#define AULOOKUP_HEADER + +#include "config.h" +#include +#include +#include "libaudit.h" +#include "ausearch-llist.h" + + +const char *aulookup_result(avc_t result); +const char *aulookup_success(int s); +const char *aulookup_syscall(llist *l, char *buf, size_t size); +const char *aulookup_uid(uid_t uid, char *buf, size_t size); +void aulookup_destroy_uid_list(void); +const char *aulookup_gid(gid_t gid, char *buf, size_t size); +void aulookup_destroy_gid_list(void); +char *unescape(const char *buf); +int is_hex_string(const char *str); +void print_tty_data(const char *val); +int need_sanitize(const unsigned char *s, unsigned int len); +void sanitize(const char *s, unsigned int len); +void safe_print_string_n(const char *s, unsigned int len, int ret); +void safe_print_string(const char *s, int ret); + +#endif + diff --git a/framework/src/audit/src/ausearch-match.c b/framework/src/audit/src/ausearch-match.c new file mode 100644 index 00000000..44c60887 --- /dev/null +++ b/framework/src/audit/src/ausearch-match.c @@ -0,0 +1,364 @@ +/* +* ausearch-match.c - Extract interesting fields and check for match +* Copyright (c) 2005-08, 2011 Red Hat Inc., Durham, North Carolina. +* Copyright (c) 2011 IBM Corp. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +* Marcelo Henrique Cerri +*/ + +#include "config.h" +#include +#include "libaudit.h" +#include "ausearch-options.h" +#include "ausearch-parse.h" + +static int strmatch(const char *needle, const char *haystack); +static int user_match(llist *l); +static int group_match(llist *l); +static int context_match(llist *l); + +/* + * This function performs that matching of search params with the record. + * It returns 1 on a match, and 0 if no match. The way that this function + * works is that it will try to determine if there is not a match and exit + * as soon as possible. We can do this since all command line params form + * an 'and' statement. If anything does not match, no need to evaluate the + * rest of the params. + */ +#include +int match(llist *l) +{ + // Are we within time range? + if (start_time == 0 || l->e.sec >= start_time) { + if (end_time == 0 || l->e.sec <= end_time) { + if (event_id == -1 || event_id == l->e.serial) { + // OK - do the heavier checking + if (extract_search_items(l)) { + return 0; + } + + // perform additional tests for the field + if (event_node_list) { + const snode *sn; + int found=0; + slist *sptr = event_node_list; + + if (l->e.node == NULL) + return 0; + + slist_first(sptr); + sn=slist_get_cur(sptr); + while (sn && !found) { + if (sn->str && (!strcmp(sn->str, l->e.node))) + found++; + else + sn=slist_next(sptr); + } + if (!found) + return 0; + } + if (user_match(l) == 0) + return 0; + if (group_match(l) == 0) + return 0; + if ((event_ppid != -1) && + (event_ppid != l->s.ppid)) + return 0; + if ((event_pid != -1) && + (event_pid != l->s.pid)) + return 0; + if (event_machine != -1 && + (event_machine != + audit_elf_to_machine(l->s.arch))) + return 0; + if ((event_syscall != -1) && + (event_syscall != l->s.syscall)) + return 0; + if ((event_session_id != -2) && + (event_session_id != l->s.session_id)) + return 0; + if (event_exit_is_set) { + if (l->s.exit_is_set == 0) + return 0; + if (event_exit != l->s.exit) + return 0; + } + + if ((event_success != S_UNSET) && + (event_success != l->s.success)) + return 0; + // event_type requires looking at each item + if (event_type != NULL) { + int found = 0; + const lnode *n; + + list_first(l); + n = list_get_cur(l); + do { + int_node *in; + ilist_first(event_type); + in = ilist_get_cur(event_type); + do { + if (in->num == n->type){ + found = 1; + break; + } + } while((in = + ilist_next(event_type))); + if (found) + break; + } while ((n = list_next(l))); + if (!found) + return 0; + } + + // Done all the easy compares, now do the + // string searches. + if (event_filename) { + int found = 0; + if (l->s.filename == NULL && l->s.cwd == NULL) + return 0; + if (l->s.filename) { + const snode *sn; + slist *sptr = l->s.filename; + + slist_first(sptr); + sn=slist_get_cur(sptr); + do { + if (sn->str == NULL) + return 0; + if (strmatch( + event_filename, + sn->str)) { + found = 1; + break; + } + } while ((sn=slist_next(sptr))); + + if (!found && l->s.cwd == NULL) + return 0; + } + if (l->s.cwd && !found) { + /* Check cwd, too */ + if (strmatch(event_filename, + l->s.cwd) == 0) + return 0; + } + } + if (event_hostname) { + if (l->s.hostname == NULL) + return 0; + if (strmatch(event_hostname, + l->s.hostname) == 0) + return 0; + } + if (event_terminal) { + if (l->s.terminal == NULL) + return 0; + if (strmatch(event_terminal, + l->s.terminal) == 0) + return 0; + } + if (event_exe) { + if (l->s.exe == NULL) + return 0; + if (strmatch(event_exe, + l->s.exe) == 0) + return 0; + } + if (event_comm) { + if (l->s.comm == NULL) + return 0; + if (strmatch(event_comm, + l->s.comm) == 0) + return 0; + } + if (event_key) { + if (l->s.key == NULL) + return 0; + else { + int found = 0; + const snode *sn; + slist *sptr = l->s.key; + + slist_first(sptr); + sn=slist_get_cur(sptr); + do { + if (sn->str == NULL) + return 0; + if (strmatch( + event_key, + sn->str)) { + found = 1; + break; + } + } while ((sn=slist_next(sptr))); + if (!found) + return 0; + } + } + if (event_vmname) { + if (l->s.vmname == NULL) + return 0; + if (strmatch(event_vmname, + l->s.vmname) == 0) + return 0; + } + if (event_uuid) { + if (l->s.uuid == NULL) + return 0; + if (strmatch(event_uuid, + l->s.uuid) == 0) + return 0; + } + if (context_match(l) == 0) + return 0; + return 1; + } + } + } + return 0; +} + +/* + * This function compares strings. It returns a 0 if no match and a 1 if + * there is a match + */ +static int strmatch(const char *needle, const char *haystack) +{ + if (event_exact_match) { + if (strcmp(haystack, needle) != 0) + return 0; + } else { + if (strstr(haystack, needle) == NULL) + return 0; + } + return 1; +} + +/* + * This function compares user id's. It returns a 0 if no match and a 1 if + * there is a match + */ +static int user_match(llist *l) +{ + if (event_ua) { + // This will "or" the user tests + if (event_uid == l->s.uid) + return 1; + if (event_euid == l->s.euid) + return 1; + if (event_loginuid == l->s.loginuid) + return 1; + return 0; + } else { + // This will "and" the user tests + if ((event_uid != -1) && (event_uid != l->s.uid)) + return 0; + if ((event_euid != -1) &&(event_euid != l->s.euid)) + return 0; + if ((event_loginuid != -2) && + (event_loginuid != l->s.loginuid)) + return 0; + } + return 1; +} + +/* + * This function compares group id's. It returns a 0 if no match and a 1 if + * there is a match + */ +static int group_match(llist *l) +{ + if (event_ga) { + // This will "or" the group tests + if (event_gid == l->s.gid) + return 1; + if (event_egid == l->s.egid) + return 1; + return 0; + } else { + // This will "and" the group tests + if ((event_gid != -1) && (event_gid != l->s.gid)) + return 0; + if ((event_egid != -1) &&(event_egid != l->s.egid)) + return 0; + } + return 1; +} + +/* + * This function compares contexts. It returns a 0 if no match and a 1 if + * there is a match + */ +static int context_match(llist *l) +{ + if (event_se) { /* This does the "or" check if -se test */ + if (event_subject) { + if (l->s.avc && alist_find_subj(l->s.avc)) { + do { + if (strmatch(event_subject, + l->s.avc->cur->scontext)) + return 1; + } while(alist_next_subj(l->s.avc)); + } + } + if (event_object) { + if (l->s.avc) { + alist_first(l->s.avc); + if (alist_find_obj(l->s.avc)) { + do { + if (strmatch(event_object, + l->s.avc->cur->tcontext)) + return 1; + } while(alist_next_obj(l->s.avc)); + } + } + } + return 0; + } else { + if (event_subject) { + if (l->s.avc == NULL) + return 0; + if (alist_find_subj(l->s.avc)) { + do { + if (strmatch(event_subject, + l->s.avc->cur->scontext)) + return 1; + } while(alist_next_subj(l->s.avc)); + } + return 0; + } + if (event_object) { + if (l->s.avc == NULL) + return 0; + if (alist_find_obj(l->s.avc)) { + do { + if (strmatch(event_object, + l->s.avc->cur->tcontext)) + return 1; + } while(alist_next_obj(l->s.avc)); + } + return 0; + } + } + return 1; +} + diff --git a/framework/src/audit/src/ausearch-nvpair.c b/framework/src/audit/src/ausearch-nvpair.c new file mode 100644 index 00000000..3dfadb60 --- /dev/null +++ b/framework/src/audit/src/ausearch-nvpair.c @@ -0,0 +1,97 @@ +/* +* ausearch-nvpair.c - Minimal linked list library for name-value pairs +* Copyright (c) 2006-08 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include "ausearch-nvpair.h" + + +void nvlist_create(nvlist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +nvnode *nvlist_next(nvlist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +void nvlist_append(nvlist *l, nvnode *node) +{ + nvnode* newnode = malloc(sizeof(nvnode)); + + newnode->name = node->name; + newnode->val = node->val; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else { // Add pointer to newnode and make sure we are at the end + while (l->cur->next) + l->cur = l->cur->next; + l->cur->next = newnode; + } + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +int nvlist_find_val(nvlist *l, long val) +{ + register nvnode* window = l->head; + + while (window) { + if (window->val == val) { + l->cur = window; + return 1; + } + else + window = window->next; + } + return 0; +} + +void nvlist_clear(nvlist* l) +{ + nvnode* nextnode; + register nvnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->name); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + diff --git a/framework/src/audit/src/ausearch-nvpair.h b/framework/src/audit/src/ausearch-nvpair.h new file mode 100644 index 00000000..9fb91ba9 --- /dev/null +++ b/framework/src/audit/src/ausearch-nvpair.h @@ -0,0 +1,57 @@ +/* +* ausearch-nvpair.h - Header file for ausearch-nvpair.c +* Copyright (c) 2006-08 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUNVPAIR_HEADER +#define AUNVPAIR_HEADER + +#include "config.h" +#include + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _nvnode{ + char *name; // The name string + long val; // The value field + struct _nvnode* next; // Next nvpair node pointer +} nvnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + nvnode *head; // List head + nvnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} nvlist; + +void nvlist_create(nvlist *l); +static inline void nvlist_first(nvlist *l) { l->cur = l->head; } +nvnode *nvlist_next(nvlist *l); +static inline nvnode *nvlist_get_cur(nvlist *l) { return l->cur; } +void nvlist_append(nvlist *l, nvnode *node); +void nvlist_clear(nvlist* l); + +/* Given a numeric index, find that record. */ +int nvlist_find_val(nvlist *l, long val); + +#endif + diff --git a/framework/src/audit/src/ausearch-options.c b/framework/src/audit/src/ausearch-options.c new file mode 100644 index 00000000..50469723 --- /dev/null +++ b/framework/src/audit/src/ausearch-options.c @@ -0,0 +1,1175 @@ +/* ausearch-options.c - parse commandline options and configure ausearch + * Copyright 2005-08,2010-11,2014 Red Hat Inc., Durham, North Carolina. + * Copyright (c) 2011 IBM Corp. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Debora Velarde + * Steve Grubb + * Marcelo Henrique Cerri + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include "ausearch-options.h" +#include "ausearch-time.h" +#include "ausearch-int.h" +#include "libaudit.h" + + +/* Global vars that will be accessed by the main program */ +char *user_file = NULL; +int force_logs = 0; + +/* Global vars that will be accessed by the match model */ +unsigned int event_id = -1; +gid_t event_gid = -1, event_egid = -1; +ilist *event_type = NULL; +pid_t event_pid = -1, event_ppid = -1; +success_t event_success = S_UNSET; +int event_exact_match = 0; +uid_t event_uid = -1, event_euid = -1, event_loginuid = -2; +int event_syscall = -1, event_machine = -1; +int event_ua = 0, event_ga = 0, event_se = 0; +int just_one = 0; +uint32_t event_session_id = -2; +long long event_exit = 0; +int event_exit_is_set = 0; +int line_buffered = 0; +int event_debug = 0; +int checkpt_timeonly = 0; +const char *event_key = NULL; +const char *event_filename = NULL; +const char *event_exe = NULL; +const char *event_comm = NULL; +const char *event_hostname = NULL; +const char *event_terminal = NULL; +const char *event_subject = NULL; +const char *event_object = NULL; +const char *event_uuid = NULL; +const char *event_vmname = NULL; +const char *checkpt_filename = NULL; /* checkpoint filename if present */ +report_t report_format = RPT_DEFAULT; +ilist *event_type; + +slist *event_node_list = NULL; + +struct nv_pair { + int value; + const char *name; +}; + + +enum { S_EVENT, S_COMM, S_FILENAME, S_ALL_GID, S_EFF_GID, S_GID, S_HELP, +S_HOSTNAME, S_INTERP, S_INFILE, S_MESSAGE_TYPE, S_PID, S_SYSCALL, S_OSUCCESS, +S_TIME_END, S_TIME_START, S_TERMINAL, S_ALL_UID, S_EFF_UID, S_UID, S_LOGINID, +S_VERSION, S_EXACT_MATCH, S_EXECUTABLE, S_CONTEXT, S_SUBJECT, S_OBJECT, +S_PPID, S_KEY, S_RAW, S_NODE, S_IN_LOGS, S_JUST_ONE, S_SESSION, S_EXIT, +S_LINEBUFFERED, S_UUID, S_VMNAME, S_DEBUG, S_CHECKPOINT, S_ARCH }; + +static struct nv_pair optiontab[] = { + { S_EVENT, "-a" }, + { S_ARCH, "--arch" }, + { S_EVENT, "--event" }, + { S_COMM, "-c" }, + { S_COMM, "--comm" }, + { S_CHECKPOINT, "--checkpoint" }, + { S_DEBUG, "--debug" }, + { S_EXIT, "-e" }, + { S_EXIT, "--exit" }, + { S_FILENAME, "-f" }, + { S_FILENAME, "--file" }, + { S_ALL_GID, "-ga" }, + { S_ALL_GID, "--gid-all" }, + { S_EFF_GID, "-ge" }, + { S_EFF_GID, "--gid-effective" }, + { S_GID, "-gi" }, + { S_GID, "--gid" }, + { S_HELP, "-h" }, + { S_HELP, "--help" }, + { S_HOSTNAME, "-hn" }, + { S_HOSTNAME, "--host" }, + { S_INTERP, "-i" }, + { S_INTERP, "--interpret" }, + { S_INFILE, "-if" }, + { S_INFILE, "--input" }, + { S_IN_LOGS, "--input-logs" }, + { S_JUST_ONE, "--just-one" }, + { S_KEY, "-k" }, + { S_KEY, "--key" }, + { S_LINEBUFFERED, "-l" }, + { S_LINEBUFFERED, "--line-buffered" }, + { S_MESSAGE_TYPE, "-m" }, + { S_MESSAGE_TYPE, "--message" }, + { S_NODE, "-n" }, + { S_NODE, "--node" }, + { S_OBJECT, "-o" }, + { S_OBJECT, "--object" }, + { S_PID, "-p" }, + { S_PID, "--pid" }, + { S_PPID, "-pp" }, + { S_PPID, "--ppid" }, + { S_RAW, "-r" }, + { S_RAW, "--raw" }, + { S_SYSCALL, "-sc" }, + { S_SYSCALL, "--syscall" }, + { S_CONTEXT, "-se" }, + { S_CONTEXT, "--context" }, + { S_SESSION, "--session" }, + { S_SUBJECT, "-su" }, + { S_SUBJECT, "--subject" }, + { S_OSUCCESS, "-sv" }, + { S_OSUCCESS, "--success" }, + { S_TIME_END, "-te"}, + { S_TIME_END, "--end"}, + { S_TIME_START, "-ts" }, + { S_TIME_START, "--start" }, + { S_TERMINAL, "-tm" }, + { S_TERMINAL, "--terminal" }, + { S_ALL_UID, "-ua" }, + { S_ALL_UID, "--uid-all" }, + { S_EFF_UID, "-ue" }, + { S_EFF_UID, "--uid-effective" }, + { S_UID, "-ui" }, + { S_UID, "--uid" }, + { S_UUID, "-uu" }, + { S_UUID, "--uuid" }, + { S_LOGINID, "-ul" }, + { S_LOGINID, "--loginuid" }, + { S_VERSION, "-v" }, + { S_VERSION, "--version" }, + { S_VMNAME, "-vm" }, + { S_VMNAME, "--vm-name" }, + { S_EXACT_MATCH, "-w" }, + { S_EXACT_MATCH, "--word" }, + { S_EXECUTABLE, "-x" }, + { S_EXECUTABLE, "--executable" } +}; +#define OPTION_NAMES (sizeof(optiontab)/sizeof(optiontab[0])) + + +static int audit_lookup_option(const char *name) +{ + int i; + + for (i = 0; i < OPTION_NAMES; i++) + if (!strcmp(optiontab[i].name, name)) + return optiontab[i].value; + return -1; +} + +static void usage(void) +{ + printf("usage: ausearch [options]\n" + "\t-a,--event \tsearch based on audit event id\n" + "\t--arch \t\t\tsearch based on the CPU architecture\n" + "\t-c,--comm \t\tsearch based on command line name\n" + "\t--checkpoint \tsearch from last complete event\n" + "\t--debug\t\t\tWrite malformed events that are skipped to stderr\n" + "\t-e,--exit \tsearch based on syscall exit code\n" + "\t-f,--file \t\tsearch based on file name\n" + "\t-ga,--gid-all \tsearch based on All group ids\n" + "\t-ge,--gid-effective search based on Effective\n\t\t\t\t\tgroup id\n" + "\t-gi,--gid \t\tsearch based on group id\n" + "\t-h,--help\t\t\thelp\n" + "\t-hn,--host \t\tsearch based on remote host name\n" + "\t-i,--interpret\t\t\tInterpret results to be human readable\n" + "\t-if,--input \tuse this file instead of current logs\n" + "\t--input-logs\t\t\tUse the logs even if stdin is a pipe\n" + "\t--just-one\t\t\tEmit just one event\n" + "\t-k,--key \t\tsearch based on key field\n" + "\t-l, --line-buffered\t\tFlush output on every line\n" + "\t-m,--message \tsearch based on message type\n" + "\t-n,--node \t\tsearch based on machine's name\n" + "\t-o,--object search based on context of object\n" + "\t-p,--pid \t\tsearch based on process id\n" + "\t-pp,--ppid \tsearch based on parent process id\n" + "\t-r,--raw\t\t\toutput is completely unformatted\n" + "\t-sc,--syscall \tsearch based on syscall name or number\n" + "\t-se,--context search based on either subject or\n\t\t\t\t\t object\n" + "\t--session \tsearch based on login session id\n" + "\t-su,--subject search based on context of the Subject\n" + "\t-sv,--success \tsearch based on syscall or event\n\t\t\t\t\tsuccess value\n" + "\t-te,--end [end date] [end time]\tending date & time for search\n" + "\t-ts,--start [start date] [start time]\tstarting data & time for search\n" + "\t-tm,--terminal \tsearch based on terminal\n" + "\t-ua,--uid-all \tsearch based on All user id's\n" + "\t-ue,--uid-effective search based on Effective\n\t\t\t\t\tuser id\n" + "\t-ui,--uid \t\tsearch based on user id\n" + "\t-ul,--loginuid \tsearch based on the User's Login id\n" + "\t-uu,--uuid \t\tsearch for events related to the virtual\n" + "\t\t\t\t\tmachine with the given UUID.\n" + "\t-v,--version\t\t\tversion\n" + "\t-vm,--vm-name \tsearch for events related to the virtual\n" + "\t\t\t\t\tmachine with the name.\n" + "\t-w,--word\t\t\tstring matches are whole word\n" + "\t-x,--executable search based on executable name\n" + ); +} + +static int convert_str_to_msg(const char *optarg) +{ + int tmp, retval = 0; + + if (isdigit(optarg[0])) { + errno = 0; + tmp = strtoul(optarg, NULL, 10); + if (errno) { + fprintf(stderr, + "Numeric message type conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + tmp = audit_name_to_msg_type(optarg); + if (tmp < 0) + retval = -1; + } + if (retval == 0) { + if (event_type == NULL) { + event_type = malloc(sizeof(ilist)); + if (event_type == NULL) + return -1; + ilist_create(event_type); + } + ilist_append(event_type, tmp, 1, 0); + } + return retval; +} + +static int parse_msg(const char *optarg) +{ + int retval = 0; + char *saved; + + if (strchr(optarg, ',')) { + char *ptr, *tmp = strdup(optarg); + if (tmp == NULL) + return -1; + ptr = strtok_r(tmp, ",", &saved); + while (ptr) { + retval = convert_str_to_msg(ptr); + if (retval != 0) + break; + ptr = strtok_r(NULL, ",", &saved); + } + free(tmp); + return retval; + } + + return convert_str_to_msg(optarg); +} + +/* + * This function examines the commandline parameters and sets various + * search options. It returns a 0 on success and < 0 on failure + */ +int check_params(int count, char *vars[]) +{ + int c = 1; + int retval = 0; + const char *optarg; + + if (count < 2) { + usage(); + return -1; + } + while (c < count && retval == 0) { + // Go ahead and point to the next argument + if (c+1 < count) { + if (vars[c+1][0] != '-') + optarg = vars[c+1]; + else + optarg = NULL; + } else + optarg = NULL; + + switch (audit_lookup_option(vars[c])) { + case S_EVENT: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_id = strtoul(optarg, NULL, 10); + if (errno) { + fprintf(stderr, + "Illegal value for audit event ID"); + retval = -1; + } + c++; + } else { + fprintf(stderr, + "Audit event id must be a numeric value, was %s\n", + optarg); + retval = -1; + } + break; + case S_COMM: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } else { + event_comm = strdup(optarg); + if (event_comm == NULL) + retval = -1; + c++; + } + break; + case S_FILENAME: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_filename = strdup(optarg); + if (event_filename == NULL) + retval = -1; + c++; + } + break; + case S_KEY: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_key = strdup(optarg); + if (event_key == NULL) + retval = -1; + c++; + } + break; + case S_ALL_GID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_gid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric group ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + struct group *gr ; + + gr = getgrnam(optarg) ; + if (gr == NULL) { + fprintf(stderr, + "Group ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_gid = gr->gr_gid; + } + event_egid = event_gid; + event_ga = 1; + c++; + break; + case S_EFF_GID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_egid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric group ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + struct group *gr ; + + gr = getgrnam(optarg) ; + if (gr == NULL) { + fprintf(stderr, + "Effective group ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_egid = gr->gr_gid; + } + c++; + break; + case S_GID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_gid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric group ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + struct group *gr ; + + gr = getgrnam(optarg) ; + if (gr == NULL) { + fprintf(stderr, + "Group ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_gid = gr->gr_gid; + } + c++; + break; + case S_HELP: + usage(); + exit(0); + break; + case S_HOSTNAME: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_hostname = strdup(optarg); + if (event_hostname == NULL) + retval = -1; + c++; + } + break; + case S_INTERP: + if (report_format == RPT_DEFAULT) + report_format = RPT_INTERP; + else { + fprintf(stderr, + "Conflicting output format %s\n", + vars[c]); + retval = -1; + } + if (optarg) { + fprintf(stderr, + "Argument is NOT required for %s\n", + vars[c]); + retval = -1; + } + break; + case S_INFILE: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + user_file = strdup(optarg); + if (user_file == NULL) + retval = -1; + c++; + } + break; + case S_MESSAGE_TYPE: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + if (strcasecmp(optarg, "ALL") != 0) { + retval = parse_msg(optarg); + } + c++; + } + if (retval < 0) { + int i; + fprintf(stderr, + "Valid message types are: ALL "); + for (i=AUDIT_USER;i<=AUDIT_LAST_VIRT_MSG;i++){ + const char *name; + if (i == AUDIT_WATCH_INS) // Skip a few + i = AUDIT_FIRST_USER_MSG; + name = audit_msg_type_to_name(i); + if (name) + fprintf(stderr, "%s ", name); + } + fprintf(stderr, "\n"); + } + break; + case S_OBJECT: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } else { + event_object = strdup(optarg); + if (event_object == NULL) + retval = -1; + c++; + } + break; + case S_PPID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_ppid = strtol(optarg,NULL,10); + if (errno) + retval = -1; + c++; + } else { + fprintf(stderr, + "Parent process id must be a numeric value, was %s\n", + optarg); + retval = -1; + } + break; + case S_PID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_pid = strtol(optarg,NULL,10); + if (errno) + retval = -1; + c++; + } else { + fprintf(stderr, + "Process id must be a numeric value, was %s\n", + optarg); + retval = -1; + } + break; + case S_RAW: + if (report_format == RPT_DEFAULT) + report_format = RPT_RAW; + else { + fprintf(stderr, + "Conflicting output format --raw\n"); + retval = -1; + } + if (optarg) { + fprintf(stderr, + "Argument is NOT required for --raw\n"); + retval = -1; + } + break; + case S_NODE: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + snode sn; + c++; + + if (!event_node_list) { + event_node_list = malloc(sizeof (slist)); + if (!event_node_list) { + retval = -1; + break; + } + slist_create(event_node_list); + } + + sn.str = strdup(optarg); + sn.key = NULL; + sn.hits=0; + slist_append(event_node_list, &sn); + } + break; + case S_SYSCALL: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_syscall = (int)strtoul(optarg, NULL, 10); + if (errno) { + fprintf(stderr, + "Syscall numeric conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + if (event_machine == -1) { + int machine; + machine = audit_detect_machine(); + if (machine < 0) { + fprintf(stderr, + "Error detecting machine type"); + retval = -1; + break; + } + event_machine = machine; + } + event_syscall = audit_name_to_syscall(optarg, + event_machine); + if (event_syscall == -1) { + fprintf(stderr, + "Syscall %s not found\n", + optarg); + retval = -1; + } + } + c++; + break; + case S_CONTEXT: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } else { + event_subject = strdup(optarg); + if (event_subject == NULL) + retval = -1; + event_object = strdup(optarg); + if (event_object == NULL) + retval = -1; + event_se = 1; + c++; + } + break; + case S_SUBJECT: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } else { + event_subject = strdup(optarg); + if (event_subject == NULL) + retval = -1; + c++; + } + break; + case S_OSUCCESS: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if ( (strstr(optarg, "yes")!=NULL) || + (strstr(optarg, "no")!=NULL) ) { + if (strcmp(optarg, "yes") == 0) + event_success = S_SUCCESS; + else + event_success = S_FAILED; + } else { + fprintf(stderr, + "Success must be 'yes' or 'no'.\n"); + retval = -1; + } + c++; + break; + case S_SESSION: + if (!optarg) { + if ((c+1 < count) && vars[c+1]) + optarg = vars[c+1]; + else { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + } + { + size_t len = strlen(optarg); + if (isdigit(optarg[0])) { + errno = 0; + event_session_id = strtoul(optarg,NULL,10); + if (errno) + retval = -1; + c++; + } else if (len >= 2 && *(optarg)=='-' && + (isdigit(optarg[1]))) { + errno = 0; + event_session_id = strtoul(optarg, NULL, 0); + if (errno) { + retval = -1; + fprintf(stderr, "Error converting %s\n", + optarg); + } + c++; + } else { + fprintf(stderr, + "Session id must be a numeric value, was %s\n", + optarg); + retval = -1; + } + } + break; + case S_EXIT: + if (!optarg) { + if ((c+1 < count) && vars[c+1]) + optarg = vars[c+1]; + else { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + } + { + size_t len = strlen(optarg); + if (isdigit(optarg[0])) { + errno = 0; + event_exit = strtoll(optarg, NULL, 0); + if (errno) { + retval = -1; + fprintf(stderr, "Error converting %s\n", + optarg); + } + } else if (len >= 2 && *(optarg)=='-' && + (isdigit(optarg[1]))) { + errno = 0; + event_exit = strtoll(optarg, NULL, 0); + if (errno) { + retval = -1; + fprintf(stderr, "Error converting %s\n", + optarg); + } + } else { + event_exit = audit_name_to_errno(optarg); + if (event_exit == 0) { + retval = -1; + fprintf(stderr, + "Unknown errno, was %s\n", + optarg); + } + } + c++; + if (retval != -1) + event_exit_is_set = 1; + } + break; + case S_TIME_END: + if (optarg) { + if ( (c+2 < count) && vars[c+2] && + (vars[c+2][0] != '-') ) { + /* Have both date and time - check order*/ + if (strchr(optarg, ':')) { + if (ausearch_time_end(vars[c+2], + optarg) != 0) + retval = -1; + } else { + if (ausearch_time_end(optarg, + vars[c+2]) != 0) + retval = -1; + } + c++; + } else { + // Check against recognized words + int t = lookup_time(optarg); + if (t >= 0) { + if (ausearch_time_end(optarg, + NULL) != 0) + retval = -1; + } else if ( (strchr(optarg, ':')) == NULL) { + /* Only have date */ + if (ausearch_time_end(optarg, + NULL) != 0) + retval = -1; + } else { + /* Only have time */ + if (ausearch_time_end(NULL, + optarg) != 0) + retval = -1; + } + } + c++; + break; + } + fprintf(stderr, + "%s requires either date and/or time\n", + vars[c]); + retval = -1; + break; + case S_TIME_START: + if (optarg) { + if ( (c+2 < count) && vars[c+2] && + (vars[c+2][0] != '-') ) { + /* Have both date and time - check order */ + if (strchr(optarg, ':')) { + if (ausearch_time_start( + vars[c+2], optarg) != 0) + retval = -1; + } else { + if (ausearch_time_start(optarg, + vars[c+2]) != 0) + retval = -1; + } + c++; + } else { + // Check against recognized words + int t = lookup_time(optarg); + if (t >= 0) { + if (ausearch_time_start(optarg, + "00:00:00") != 0) + retval = -1; + } else if (strcmp(optarg, + "checkpoint") == 0) { + // Only use the timestamp from within + // the checkpoint file + checkpt_timeonly++; + } else if (strchr(optarg, ':') == NULL){ + /* Only have date */ + if (ausearch_time_start(optarg, + "00:00:00") != 0) + retval = -1; + } else { + /* Only have time */ + if (ausearch_time_start(NULL, + optarg) != 0) + retval = -1; + } + } + c++; + break; + } + fprintf(stderr, + "%s requires either date and/or time\n", + vars[c]); + retval = -1; + break; + case S_TERMINAL: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_terminal = strdup(optarg); + if (event_terminal == NULL) + retval = -1; + c++; + } + break; + case S_UID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_uid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric user ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + struct passwd *pw; + + pw = getpwnam(optarg); + if (pw == NULL) { + fprintf(stderr, + "Effective user ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_uid = pw->pw_uid; + } + c++; + break; + case S_EFF_UID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_euid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric user ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + struct passwd *pw ; + + pw = getpwnam(optarg) ; + if (pw == NULL) { + fprintf(stderr, + "User ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_euid = pw->pw_uid; + } + c++; + break; + case S_ALL_UID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (isdigit(optarg[0])) { + errno = 0; + event_uid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric user ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else { + struct passwd *pw ; + + pw = getpwnam(optarg) ; + if (pw == NULL) { + fprintf(stderr, + "User ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_uid = pw->pw_uid; + } + event_ua = 1; + event_euid = event_uid; + event_loginuid = event_uid; + c++; + break; + case S_LOGINID: + if (!optarg) { + if ((c+1 < count) && vars[c+1]) + optarg = vars[c+1]; + else { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + } + { + size_t len = strlen(optarg); + if (isdigit(optarg[0])) { + errno = 0; + event_loginuid = strtoul(optarg,NULL,10); + if (errno) { + fprintf(stderr, + "Numeric user ID conversion error (%s) for %s\n", + strerror(errno), optarg); + retval = -1; + } + } else if (len >= 2 && *(optarg)=='-' && + (isdigit(optarg[1]))) { + errno = 0; + event_loginuid = strtol(optarg, NULL, 0); + if (errno) { + retval = -1; + fprintf(stderr, "Error converting %s\n", + optarg); + } + } else { + struct passwd *pw ; + + pw = getpwnam(optarg) ; + if (pw == NULL) { + fprintf(stderr, + "Login user ID is non-numeric and unknown (%s)\n", + optarg); + retval = -1; + break; + } + event_loginuid = pw->pw_uid; + } + } + c++; + break; + case S_UUID: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_uuid = strdup(optarg); + if (event_uuid == NULL) { + retval = -1; + } + c++; + } + break; + case S_VMNAME: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_vmname= strdup(optarg); + if (event_vmname == NULL) { + retval = -1; + } + c++; + } + break; + case S_VERSION: + printf("ausearch version %s\n", VERSION); + exit(0); + break; + case S_EXACT_MATCH: + event_exact_match=1; + break; + case S_IN_LOGS: + force_logs = 1; + break; + case S_JUST_ONE: + just_one = 1; + break; + case S_EXECUTABLE: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + event_exe = strdup(optarg); + if (event_exe == NULL) + retval = -1; + c++; + } + break; + case S_LINEBUFFERED: + line_buffered = 1; + break; + case S_DEBUG: + event_debug = 1; + break; + case S_CHECKPOINT: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + } else { + checkpt_filename = strdup(optarg); + if (checkpt_filename == NULL) + retval = -1; + c++; + } + break; + case S_ARCH: + if (!optarg) { + fprintf(stderr, + "Argument is required for %s\n", + vars[c]); + retval = -1; + break; + } + if (event_machine != -1) { + if (event_syscall != -1) + fprintf(stderr, + "Arch needs to be defined before the syscall\n"); + else + fprintf(stderr, + "Arch is already defined\n"); + retval = -1; + break; + } else { + int machine = audit_determine_machine(optarg); + if (machine < 0) { + fprintf(stderr, "Unknown arch %s\n", + optarg); + retval = -1; + } + event_machine = machine; + } + c++; + break; + default: + fprintf(stderr, "%s is an unsupported option\n", + vars[c]); + retval = -1; + break; + } + c++; + } + + return retval; +} + diff --git a/framework/src/audit/src/ausearch-options.h b/framework/src/audit/src/ausearch-options.h new file mode 100644 index 00000000..1372762b --- /dev/null +++ b/framework/src/audit/src/ausearch-options.h @@ -0,0 +1,52 @@ +/* ausearch-options.h -- + * Copyright 2005,2008,2010 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + * + */ + +#ifndef AUSEARCH_OPTIONS_H +#define AUSEARCH_OPTIONS_H + +#include +#include +#include +#include "ausearch-common.h" +#include "ausearch-int.h" + +/* Global variables that describe what search is to be performed */ +extern const char *event_key; +extern const char *event_subject; +extern const char *event_object; +extern int event_se; +extern int just_one; +extern int line_buffered; +extern int event_debug; +extern pid_t event_ppid; +extern uint32_t event_session_id; +extern ilist *event_type; + +/* Data type to govern output format */ +extern report_t report_format; + +/* Function to process commandline options */ +extern int check_params(int count, char *vars[]); + +#endif + diff --git a/framework/src/audit/src/ausearch-parse.c b/framework/src/audit/src/ausearch-parse.c new file mode 100644 index 00000000..1fb1c151 --- /dev/null +++ b/framework/src/audit/src/ausearch-parse.c @@ -0,0 +1,2310 @@ +/* +* ausearch-parse.c - Extract interesting fields and check for match +* Copyright (c) 2005-08,2011,2013-14 Red Hat Inc., Durham, North Carolina. +* Copyright (c) 2011 IBM Corp. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +* Marcelo Henrique Cerri +*/ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include /* PATH_MAX */ +#include +#include "libaudit.h" +#include "ausearch-options.h" +#include "ausearch-lookup.h" +#include "ausearch-parse.h" + +#define NAME_OFFSET 36 +static const char key_sep[2] = { AUDIT_KEY_SEPARATOR, 0 }; + +static int parse_syscall(lnode *n, search_items *s); +static int parse_dir(const lnode *n, search_items *s); +static int common_path_parser(search_items *s, char *path); +static int avc_parse_path(const lnode *n, search_items *s); +static int parse_path(const lnode *n, search_items *s); +static int parse_user(const lnode *n, search_items *s); +static int parse_obj(const lnode *n, search_items *s); +static int parse_login(const lnode *n, search_items *s); +static int parse_daemon1(const lnode *n, search_items *s); +static int parse_daemon2(const lnode *n, search_items *s); +static int parse_sockaddr(const lnode *n, search_items *s); +static int parse_avc(const lnode *n, search_items *s); +static int parse_integrity(const lnode *n, search_items *s); +static int parse_kernel_anom(const lnode *n, search_items *s); +static int parse_simple_message(const lnode *n, search_items *s); +static int parse_tty(const lnode *n, search_items *s); +static int parse_pkt(const lnode *n, search_items *s); + + +static int audit_avc_init(search_items *s) +{ + if (s->avc == NULL) { + //create + s->avc = malloc(sizeof(alist)); + if (s->avc == NULL) + return -1; + alist_create(s->avc); + } + return 0; +} + +/* + * This function will take the list and extract the searchable fields from it. + * It returns 0 on success and 1 on failure. + */ +int extract_search_items(llist *l) +{ + int ret = 0; + lnode *n; + search_items *s = &l->s; + list_first(l); + n = list_get_cur(l); + if (n) { + do { + switch (n->type) { + case AUDIT_SYSCALL: + ret = parse_syscall(n, s); + break; + case AUDIT_CWD: + ret = parse_dir(n, s); + break; + case AUDIT_AVC_PATH: + ret = avc_parse_path(n, s); + break; + case AUDIT_PATH: + ret = parse_path(n, s); + break; + case AUDIT_USER: + case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: + case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: + ret = parse_user(n, s); + break; + case AUDIT_SOCKADDR: + ret = parse_sockaddr(n, s); + break; + case AUDIT_LOGIN: + ret = parse_login(n, s); + break; + case AUDIT_IPC: + case AUDIT_OBJ_PID: + ret = parse_obj(n, s); + break; + case AUDIT_DAEMON_START: + case AUDIT_DAEMON_END: + case AUDIT_DAEMON_ABORT: + case AUDIT_DAEMON_CONFIG: + case AUDIT_DAEMON_ROTATE: + case AUDIT_DAEMON_RESUME: + ret = parse_daemon1(n, s); + break; + case AUDIT_DAEMON_ACCEPT: + case AUDIT_DAEMON_CLOSE: + ret = parse_daemon2(n, s); + break; + case AUDIT_CONFIG_CHANGE: + ret = parse_simple_message(n, s); + // We use AVC parser because it just looks for + // the one field. We don't care about return + // code since older events don't have path= + avc_parse_path(n, s); + break; + case AUDIT_AVC: + ret = parse_avc(n, s); + break; + case AUDIT_NETFILTER_PKT: + ret = parse_pkt(n, s); + break; + case AUDIT_SECCOMP: + case + AUDIT_FIRST_KERN_ANOM_MSG...AUDIT_LAST_KERN_ANOM_MSG: + ret = parse_kernel_anom(n, s); + break; + case AUDIT_MAC_POLICY_LOAD...AUDIT_MAC_UNLBL_STCDEL: + ret = parse_simple_message(n, s); + break; + case AUDIT_INTEGRITY_DATA...AUDIT_INTEGRITY_RULE: + ret = parse_integrity(n, s); + break; + case AUDIT_KERNEL: + case AUDIT_SELINUX_ERR: + case AUDIT_EXECVE: + case AUDIT_IPC_SET_PERM: + case AUDIT_MQ_OPEN: + case AUDIT_MQ_SENDRECV: + case AUDIT_MQ_NOTIFY: + case AUDIT_MQ_GETSETATTR: + case AUDIT_FD_PAIR: + case AUDIT_BPRM_FCAPS: + case AUDIT_CAPSET: + case AUDIT_MMAP: + case AUDIT_NETFILTER_CFG: + // Nothing to parse + break; + case AUDIT_TTY: + ret = parse_tty(n, s); + break; + default: + if (event_debug) + fprintf(stderr, + "Unparsed type:%d\n - skipped", + n->type); + break; + } + if (event_debug && ret) + fprintf(stderr, + "Malformed event skipped, rc=%d. %s\n", + ret, n->message); + } while ((n=list_next(l)) && ret == 0); + } + return ret; +} + +static int parse_syscall(lnode *n, search_items *s) +{ + char *ptr, *str, *term; + extern int event_machine; + + term = n->message; + if (report_format > RPT_DEFAULT || event_machine != -1) { + // get arch + str = strstr(term, "arch="); + if (str == NULL) + return 1; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 2; + *term = 0; + errno = 0; + s->arch = (int)strtoul(ptr, NULL, 16); + if (errno) + return 3; + *term = ' '; + } + // get syscall + str = strstr(term, "syscall="); + if (str == NULL) + return 4; + ptr = str + 8; + term = strchr(ptr, ' '); + if (term == NULL) + return 5; + *term = 0; + errno = 0; + s->syscall = (int)strtoul(ptr, NULL, 10); + if (errno) + return 6; + *term = ' '; + // get success + if (event_success != S_UNSET) { + str = strstr(term, "success="); + if (str) { // exit_group does not set success !?! + ptr = str + 8; + term = strchr(ptr, ' '); + if (term == NULL) + return 7; + *term = 0; + if (strcmp(ptr, "yes") == 0) + s->success = S_SUCCESS; + else + s->success = S_FAILED; + *term = ' '; + } + } + // get exit + if (event_exit_is_set) { + str = strstr(term, "exit="); + if (str == NULL) + return 8; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 9; + *term = 0; + errno = 0; + s->exit = strtoll(ptr, NULL, 0); + if (errno) + return 10; + s->exit_is_set = 1; + *term = ' '; + } + // get a0 + str = strstr(term, "a0="); + if (str == NULL) + return 11; + ptr = str + 3; + term = strchr(ptr, ' '); + if (term == NULL) + return 12; + *term = 0; + errno = 0; + // 64 bit dump on 32 bit machine looks bad here - need long long + n->a0 = strtoull(ptr, NULL, 16); // Hex + if (errno) + return 13; + *term = ' '; + // get a1 + str = strstr(term, "a1="); + if (str == NULL) + return 11; + ptr = str + 3; + term = strchr(ptr, ' '); + if (term == NULL) + return 12; + *term = 0; + errno = 0; + // 64 bit dump on 32 bit machine looks bad here - need long long + n->a1 = strtoull(ptr, NULL, 16); // Hex + if (errno) + return 13; + *term = ' '; + // ppid + if (event_ppid != -1) { + str = strstr(term, "ppid="); + if (str != NULL) { // ppid is an optional field + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 14; + *term = 0; + errno = 0; + s->ppid = strtoul(ptr, NULL, 10); + if (errno) + return 15; + *term = ' '; + } + } + // pid + if (event_pid != -1) { + str = strstr(term, " pid="); + if (str == NULL) + return 16; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 17; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 18; + *term = ' '; + } + // optionally get loginuid + if (event_loginuid != -2) { + str = strstr(term, "auid="); + if (str == NULL) { + str = strstr(term, "loginuid="); + if (str == NULL) + return 19; + ptr = str + 9; + } else + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 20; + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 21; + *term = ' '; + } + // optionally get uid + if (event_uid != -1) { + str = strstr(term, "uid="); + if (str == NULL) + return 22; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 23; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 24; + *term = ' '; + } + + // optionally get gid + if (event_gid != -1) { + str = strstr(term, "gid="); + if (str == NULL) + return 25; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 26; + *term = 0; + errno = 0; + s->gid = strtoul(ptr, NULL, 10); + if (errno) + return 27; + *term = ' '; + } + + // euid + if (event_euid != -1) { + str = strstr(term, "euid="); + if (str == NULL) + return 28; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 29; + *term = 0; + errno = 0; + s->euid = strtoul(ptr, NULL, 10); + if (errno) + return 30; + *term = ' '; + } + + // egid + if (event_egid != -1) { + str = strstr(term, "egid="); + if (str == NULL) + return 31; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 32; + *term = 0; + errno = 0; + s->egid = strtoul(ptr, NULL, 10); + if (errno) + return 33; + *term = ' '; + } + + if (event_terminal) { + // dont do this search unless needed + str = strstr(term, "tty="); + if (str) { + str += 4; + term = strchr(str, ' '); + if (term == NULL) + return 34; + *term = 0; + s->terminal = strdup(str); + *term = ' '; + } + } + // ses + if (event_session_id != -2 ) { + str = strstr(term, "ses="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 35; + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 36; + *term = ' '; + } + } + if (event_comm) { + // dont do this search unless needed + str = strstr(term, "comm="); + if (str) { + /* Make the syscall one override */ + if (s->comm) + free(s->comm); + str += 5; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 37; + *term = 0; + s->comm = strdup(str); + *term = '"'; + } else + s->comm = unescape(str); + } else + return 38; + } + if (event_exe) { + // dont do this search unless needed + str = strstr(n->message, "exe="); + if (str) { + str += 4; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 39; + *term = 0; + s->exe = strdup(str); + *term = '"'; + } else + s->exe = unescape(str); + } else + return 40; + } + if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str != NULL) { + str += 5; + term = strchr(str, ' '); + if (term == NULL) + return 41; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 42; + } + } + if (event_key) { + str = strstr(term, "key="); + if (str) { + if (!s->key) { + //create + s->key = malloc(sizeof(slist)); + if (s->key == NULL) + return 43; + slist_create(s->key); + } + str += 4; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 44; + *term = 0; + if (s->key) { + // append + snode sn; + sn.str = strdup(str); + sn.key = NULL; + sn.hits = 1; + slist_append(s->key, &sn); + } + *term = '"'; + } else { + if (s->key) { + char *saved; + char *keyptr = unescape(str); + char *kptr = strtok_r(keyptr, + key_sep, &saved); + while (kptr) { + snode sn; + // append + sn.str = strdup(kptr); + sn.key = NULL; + sn.hits = 1; + slist_append(s->key, &sn); + kptr = strtok_r(NULL, + key_sep, &saved); + } + free(keyptr); + + } + } + } + } + return 0; +} + +static int parse_dir(const lnode *n, search_items *s) +{ + char *str, *term; + + if (event_filename) { + // dont do this search unless needed + str = strstr(n->message+NAME_OFFSET, " cwd="); + if (str) { + str += 5; + if (*str == '"') { + /* string is normal */ + str++; + term = strchr(str, '"'); + if (term == NULL) + return 1; + *term = 0; + if (!s->cwd) + s->cwd = strdup(str); + *term = '"'; + } else if (!s->cwd) + s->cwd = unescape(str); + } + } + return 0; +} + +static int common_path_parser(search_items *s, char *path) +{ + char *term; + + if (!s->filename) { + //create + s->filename = malloc(sizeof(slist)); + if (s->filename == NULL) + return 1; + slist_create(s->filename); + } + if (*path == '"') { + /* string is normal */ + path++; + term = strchr(path, '"'); + if (term == NULL) + return 2; + *term = 0; + if (s->filename) { + // append + snode sn; + sn.str = strdup(path); + sn.key = NULL; + sn.hits = 1; + // Attempt to rebuild path if relative + if ((sn.str[0] == '.') && ((sn.str[1] == '.') || + (sn.str[1] == '/')) && s->cwd) { + char *tmp = malloc(PATH_MAX); + if (tmp == NULL) { + free(sn.str); + return 3; + } + snprintf(tmp, PATH_MAX, + "%s/%s", s->cwd, sn.str); + free(sn.str); + sn.str = tmp; + } + slist_append(s->filename, &sn); + } + *term = '"'; + } else { + if (s->filename) { + // append + snode sn; + sn.key = NULL; + sn.hits = 1; + if (strncmp(path, "(null)", 6) == 0) { + sn.str = strdup("(null)"); + goto append; + } + if (!isxdigit(path[0])) + return 4; + if (path[0] == '0' && path[1] == '0') + sn.str = unescape(&path[2]); // Abstract name + else { + term = strchr(path, ' '); + if (term == NULL) + return 5; + *term = 0; + sn.str = unescape(path); + *term = ' '; + } + // Attempt to rebuild path if relative + if ((sn.str[0] == '.') && ((sn.str[1] == '.') || + (sn.str[1] == '/')) && s->cwd) { + char *tmp = malloc(PATH_MAX); + if (tmp == NULL) + return 6; + snprintf(tmp, PATH_MAX, "%s/%s", + s->cwd, sn.str); + free(sn.str); + sn.str = tmp; + } +append: + slist_append(s->filename, &sn); + } + } + return 0; +} + +/* Older AVCs have path separate from the AVC record */ +static int avc_parse_path(const lnode *n, search_items *s) +{ + char *str; + + if (event_filename) { + // dont do this search unless needed + str = strstr(n->message, " path="); + if (str) { + str += 6; + return common_path_parser(s, str); + } + return 1; + } + return 0; +} + +static int parse_path(const lnode *n, search_items *s) +{ + // We add 32 to message because we do not nee to look at + // anything before that. Its only time and type. + char *str, *term = n->message+NAME_OFFSET; + + if (event_filename) { + // dont do this search unless needed + str = strstr(term, " name="); + if (str) { + int rc; + str += 6; + rc = common_path_parser(s, str); + if (rc) + return rc; + } + } + if (event_object) { + // tcontext + str = strstr(term, "obj="); + if (str != NULL) { + str += 4; + term = strchr(str, ' '); + if (term) + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.tcontext = strdup(str); + alist_append(s->avc, &an); + if (term) + *term = ' '; + } else + return 7; + } + } + return 0; +} + +static int parse_obj(const lnode *n, search_items *s) +{ + char *str, *term; + + term = n->message; + if (event_object) { + // obj context + str = strstr(term, "obj="); + if (str != NULL) { + str += 4; + term = strchr(str, ' '); + if (term) + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.tcontext = strdup(str); + alist_append(s->avc, &an); + if (term) + *term = ' '; + } else + return 1; + } + } + return 0; +} + +static int parse_user(const lnode *n, search_items *s) +{ + char *ptr, *str, *term, saved, *mptr; + + term = n->message; + + // get pid + if (event_pid != -1) { + str = strstr(term, "pid="); + if (str == NULL) + return 1; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 2; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 3; + *term = ' '; + } + // optionally get uid + if (event_uid != -1) { + str = strstr(term, "uid="); + if (str == NULL) + return 4; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 5; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 6; + *term = ' '; + } + // optionally get loginuid + if (event_loginuid != -2) { + *term = ' '; + str = strstr(term, "auid="); + if (str == NULL) { // Try the older one + str = strstr(term, "loginuid="); + if (str == NULL) + return 7; + ptr = str + 9; + } else + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 8; + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 9; + *term = ' '; + } + // ses + if (event_session_id != -2 ) { + str = strstr(term, "ses="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 10; + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 11; + *term = ' '; + } + } + if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str != NULL) { + str += 5; + term = strchr(str, ' '); + if (term == NULL) + return 12; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 13; + } + } + // optionally get gid + if (event_gid != -1) { + if (n->type == AUDIT_ADD_GROUP || n->type == AUDIT_DEL_GROUP || + n->type == AUDIT_GRP_MGMT) { + str = strstr(term, " id="); + // Take second shot in the case of MGMT events + if (str == NULL && n->type == AUDIT_GRP_MGMT) + str = strstr(term, "gid="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 31; + *term = 0; + errno = 0; + s->gid = strtoul(ptr, NULL, 10); + if (errno) + return 32; + *term = ' '; + } + } + } + if (event_vmname) { + str = strstr(term, "vm="); + if (str) { + str += 3; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 23; + *term = 0; + s->vmname = strdup(str); + *term = '"'; + } else + s->vmname = unescape(str); + } + } + if (event_uuid) { + str = strstr(term, "uuid="); + if (str) { + str += 5; + term = str; + while (*term != ' ' && *term != ':') + term++; + if (term == str) + return 24; + saved = *term; + *term = 0; + s->uuid = strdup(str); + *term = saved; + } + } + if (event_subject) { + str = strstr(term, "vm-ctx="); + if (str != NULL) { + str += 7; + term = strchr(str, ' '); + if (term == NULL) + return 27; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 28; + } + } + if (event_object) { + str = strstr(term, "img-ctx="); + if (str != NULL) { + str += 8; + term = strchr(str, ' '); + if (term == NULL) + return 29; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.tcontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 30; + } + } + // optionally get uid - some records the second uid is what we want. + // USER_LOGIN for example. + if (event_uid != -1) { + str = strstr(term, "uid="); + if (str) { + if (*(str - 1) == 'a' || *(str - 1) == 's' || + *(str - 1) == 'u') + goto skip; + if (!(*(str - 1) == '\'' || *(str - 1) == ' ')) + return 25; + ptr = str + 4; + term = ptr; + while (isdigit(*term)) + term++; + if (term == ptr) + return 14; + + saved = *term; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 15; + *term = saved; + } + } +skip: + mptr = term + 1; + + if (event_comm) { + // dont do this search unless needed + str = strstr(mptr, "comm="); + if (str) { + str += 5; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 16; + *term = 0; + s->comm = strdup(str); + *term = '"'; + } else + s->comm = unescape(str); + } + } + + // Get acct for user/group add/del + str = strstr(mptr, "acct="); + if (str != NULL) { + ptr = str + 5; + term = ptr + 1; + if (*ptr == '"') { + while (*term != '"') + term++; + saved = *term; + *term = 0; + ptr++; + s->acct = strdup(ptr); + *term = saved; + } else { + /* Handle legacy accts */ + char *end = ptr; + int legacy = 0; + + while (*end != ' ') { + if (!isxdigit(*end)) + legacy = 1; + end++; + } + term = end; + if (!legacy) + s->acct = unescape(ptr); + else { + saved = *term; + *term = 0; + s->acct = strdup(ptr); + *term = saved; + } + } + } + mptr = term + 1; + + // get hostname + if (event_hostname) { + // dont do this search unless needed + str = strstr(mptr, "hostname="); + if (str) { + str += 9; + term = strchr(str, ','); + if (term == NULL) { + term = strchr(str, ' '); + if (term == NULL) + return 17; + } + saved = *term; + *term = 0; + s->hostname = strdup(str); + *term = saved; + + // Lets see if there is something more + // meaningful in addr + if (strcmp(s->hostname, "?") == 0) { + term++; + str = strstr(term, "addr="); + if (str) { + str += 5; + term = strchr(str, ','); + if (term == NULL) { + term = strchr(str, ' '); + if (term == NULL) + return 18; + } + saved = *term; + *term = 0; + free(s->hostname); + s->hostname = strdup(str); + *term = saved; + } + } + } + } + if (event_filename) { + // dont do this search unless needed + str = strstr(mptr, "cwd="); + if (str) { + str += 4; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 20; + *term = 0; + s->cwd = strdup(str); + *term = '"'; + } else { + char *end = str; + int legacy = 0; + + while (*end != ' ') { + if (!isxdigit(*end)) { + legacy = 1; + } + end++; + } + term = end; + if (!legacy) + s->cwd = unescape(str); + else { + saved = *term; + *term = 0; + s->cwd = strdup(str); + *term = saved; + } + } + } + } + if (event_terminal) { + // dont do this search unless needed + str = strstr(mptr, "terminal="); + if (str) { + str += 9; + term = strchr(str, ' '); + if (term == NULL) { + term = strchr(str, ')'); + if (term == NULL) + return 19; + } + *term = 0; + s->terminal = strdup(str); + *term = ' '; + } + } + if (event_exe) { + // dont do this search unless needed + str = strstr(mptr, "exe="); + if (str) { + str += 4; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 26; + *term = 0; + s->exe = strdup(str); + *term = '"'; + } else { + char *end = str; + int legacy = 0; + + while (*end != ' ') { + if (!isxdigit(*end)) { + legacy = 1; + } + end++; + } + term = end; + if (!legacy) + s->exe = unescape(str); + else { + saved = *term; + *term = 0; + s->exe = strdup(str); + *term = saved; + } + } + } + } + + // get success + if (event_success != S_UNSET) { + str = strstr(mptr, "res="); + if (str) { + ptr = str + 4; + term = strchr(ptr, '\''); + if (term == NULL) + return 21; + *term = 0; + if (strncmp(ptr, "failed", 6) == 0) + s->success = S_FAILED; + else + s->success = S_SUCCESS; + *term = '\''; + } else if ((str = strstr(mptr, "result="))) { + ptr = str + 7; + term = strchr(ptr, ')'); + if (term == NULL) + return 22; + *term = 0; + if (strcasecmp(ptr, "success") == 0) + s->success = S_SUCCESS; + else + s->success = S_FAILED; + *term = ')'; + } + } + /* last return code used = 24 */ + return 0; +} + +static int parse_login(const lnode *n, search_items *s) +{ + char *ptr, *str, *term = n->message; + + // get pid + if (event_pid != -1) { + str = strstr(term, "pid="); + if (str == NULL) + return 1; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 2; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 3; + *term = ' '; + } + // optionally get uid + if (event_uid != -1) { + str = strstr(term, "uid="); + if (str == NULL) + return 4; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 5; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 6; + *term = ' '; + } + // optionally get subj + if (event_subject) { + str = strstr(term, "subj="); + if (str) { + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 12; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 13; + *term = ' '; + } + } + // optionally get loginuid + if (event_loginuid != -2) { + str = strstr(term, "new auid="); + if (str == NULL) { + // 3.14 kernel changed it to the next line + str = strstr(term, " auid="); + if (str == NULL) { + str = strstr(term, "new loginuid="); + if (str == NULL) + return 7; + ptr = str + 13; + } else + ptr = str + 6; + } else + ptr = str + 9; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 8; + if (term) + *term = ' '; + } + + // success + if (event_success != S_UNSET) { + if (term == NULL) + term = n->message; + str = strstr(term, "res="); + if (str != NULL) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->success = strtoul(ptr, NULL, 10); + if (errno) + return 9; + if (term) + *term = ' '; + } else // Assume older kernel where always successful + s->success = S_SUCCESS; + } + // ses + if (event_session_id != -2 ) { + if (term == NULL) + term = n->message; + str = strstr(term, "new ses="); + if (str == NULL) { + // The 3.14 kernel changed it to the next line + str = strstr(term, " ses="); + if (str == NULL) + return 14; + ptr = str + 5; + } + else + ptr = str + 8; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 11; + if (term) + *term = ' '; + } + return 0; +} + +static int parse_daemon1(const lnode *n, search_items *s) +{ + char *ptr, *str, *term, saved, *mptr; + + // Not all messages have a ')', use it if its there + mptr = strchr(n->message, ')'); + if (mptr == NULL) + mptr = n->message; + term = mptr; + + // optionally get auid + if (event_loginuid != -2 ) { + str = strstr(mptr, "auid="); + if (str == NULL) + return 1; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 2; + saved = *term; + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 3; + *term = saved; + } + + // pid + if (event_pid != -1) { + str = strstr(term, "pid="); + if (str == NULL) + return 4; + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 5; + saved = *term; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 6; + *term = saved; + } + + if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str != NULL) { + str += 5; + term = strchr(str, ' '); + if (term) + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + } else + return 7; + if (term) + *term = ' '; + } + } + + // success + if (event_success != S_UNSET) { + str = strstr(mptr, "res="); + if (str) { + ptr = term = str + 4; + while (isalpha(*term)) + term++; + if (term == ptr) + return 9; + saved = *term; + *term = 0; + if (strncmp(ptr, "failed", 6) == 0) + s->success = S_FAILED; + else + s->success = S_SUCCESS; + *term = saved; + } + } + + return 0; +} + +static int parse_daemon2(const lnode *n, search_items *s) +{ + char *str, saved, *term = n->message; + + if (event_hostname) { + str = strstr(term, "addr="); + if (str) { + str += 5; + term = strchr(str, ':'); + if (term == NULL) { + term = strchr(str, ' '); + if (term == NULL) + return 1; + } + saved = *term; + *term = 0; + free(s->hostname); + s->hostname = strdup(str); + *term = saved; + } + } + + if (event_success != S_UNSET) { + char *str = strstr(term, "res="); + if (str) { + char *ptr, *term, saved; + + ptr = term = str + 4; + while (isalpha(*term)) + term++; + if (term == ptr) + return 2; + saved = *term; + *term = 0; + if (strncmp(ptr, "failed", 6) == 0) + s->success = S_FAILED; + else + s->success = S_SUCCESS; + *term = saved; + } + } + + return 0; +} + +static int parse_sockaddr(const lnode *n, search_items *s) +{ + char *str; + + if (event_hostname || event_filename) { + str = strstr(n->message, "saddr="); + if (str) { + int len; + struct sockaddr *saddr; + char name[NI_MAXHOST]; + + str += 6; + len = strlen(str)/2; + s->hostname = unescape(str); + saddr = (struct sockaddr *)s->hostname; + if (saddr->sa_family == AF_INET) { + if (len < sizeof(struct sockaddr_in)) { + fprintf(stderr, + "sockaddr len too short\n"); + return 1; + } + len = sizeof(struct sockaddr_in); + } else if (saddr->sa_family == AF_INET6) { + if (len < sizeof(struct sockaddr_in6)) { + fprintf(stderr, + "sockaddr6 len too short\n"); + return 2; + } + len = sizeof(struct sockaddr_in6); + } else if (saddr->sa_family == AF_UNIX) { + struct sockaddr_un *un = + (struct sockaddr_un *)saddr; + if (un->sun_path[0]) + len = strlen(un->sun_path); + else // abstract name + len = strlen(&un->sun_path[1]); + if (len == 0) { + fprintf(stderr, + "sun_path len too short\n"); + return 3; + } + if (event_filename) { + if (!s->filename) { + //create + s->filename = + malloc(sizeof(slist)); + if (s->filename == NULL) + return 4; + slist_create(s->filename); + } + if (s->filename) { + // append + snode sn; + sn.str = strdup(un->sun_path); + sn.key = NULL; + sn.hits = 1; + slist_append(s->filename, &sn); + } + free(s->hostname); + s->hostname = NULL; + return 0; + } else { // No file name - no need for socket + free(s->hostname); + s->hostname = NULL; + return 0; + } + } else { + // addr family we don't care about + free(s->hostname); + s->hostname = NULL; + return 0; + } + if (!event_hostname) { + // we entered here for files - discard + free(s->hostname); + s->hostname = NULL; + return 0; + } + if (getnameinfo(saddr, len, name, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST) ) { + free(s->hostname); + s->hostname = NULL; + } else { + free(s->hostname); + s->hostname = strdup(name); + } + } + } + return 0; +} + +static int parse_integrity(const lnode *n, search_items *s) +{ + char *ptr, *str, *term; + + term = n->message; + // get pid + str = strstr(term, "pid="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 1; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 2; + *term = ' '; + } + + // optionally get uid + if (event_uid != -1) { + str = strstr(term, " uid="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 3; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 4; + *term = ' '; + } + } + + // optionally get loginuid + if (event_loginuid != -2) { + str = strstr(n->message, "auid="); + if (str) { + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 5; + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 6; + *term = ' '; + } + } + + // ses + if (event_session_id != -2 ) { + str = strstr(term, "ses="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 10; + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 11; + *term = ' '; + } + } + + if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str) { + str += 5; + term = strchr(str, ' '); + if (term == NULL) + return 12; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 13; + } + } + + if (event_comm) { + str = strstr(term, "comm="); + if (str) { + str += 5; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 7; + *term = 0; + s->comm = strdup(str); + *term = '"'; + } else + s->comm = unescape(str); + } + } + + if (event_filename) { + str = strstr(term, " name="); + if (str) { + str += 6; + if (common_path_parser(s, str)) + return 8; + } + } + + // and results (usually last) + if (event_success != S_UNSET) { + str = strstr(term, "res="); + if (str != NULL) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->success = strtoul(ptr, NULL, 10); + if (errno) + return 9; + if (term) + *term = ' '; + } + } + + return 0; +} + + +/* FIXME: If they are in permissive mode or hit an auditallow, there can + * be more that 1 avc in the same syscall. For now, we pickup just the first. + */ +static int parse_avc(const lnode *n, search_items *s) +{ + char *str, *term; + anode an; + int rc=0; + + term = n->message; + anode_init(&an); + + // get the avc message info. + str = strstr(term, "avc: "); + if (str) { + str += 5; + term = strchr(str, '{'); + if (term == NULL) + return 1; + if (event_success != S_UNSET) { + *term = 0; + // FIXME. Do not override syscall success if already + // set. Syscall pass/fail is the authoritative value. + if (strstr(str, "denied")) { + s->success = S_FAILED; + an.avc_result = AVC_DENIED; + } else { + s->success = S_SUCCESS; + an.avc_result = AVC_GRANTED; + } + *term = '{'; + } + + // Now get permission + str = term + 1; + while (*str == ' ') + str++; + term = strchr(str, '}'); + if (term == NULL) + return 2; + while (*(term-1) == ' ') + term--; + *term = 0; + an.avc_perm = strdup(str); + *term = ' '; + } + + // get pid + if (event_pid != -1) { + str = strstr(term, "pid="); + if (str) { + str = str + 4; + term = strchr(str, ' '); + if (term == NULL) { + rc = 3; + goto err; + } + *term = 0; + errno = 0; + s->pid = strtoul(str, NULL, 10); + if (errno) { + rc = 4; + goto err; + } + *term = ' '; + } + } + + if (event_comm && s->comm == NULL) { + // dont do this search unless needed + str = strstr(term, "comm="); + if (str == NULL) { + rc = 5; + goto err; + } + str += 5; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) { + rc = 6; + goto err; + } + *term = 0; + s->comm = strdup(str); + *term = '"'; + } else { + s->comm = unescape(str); + term = str + 6; + } + } + if (event_filename) { + // do we have a path? + str = strstr(term, " path="); + if (str) { + str += 6; + rc = common_path_parser(s, str); + if (rc) + goto err; + term += 7; + } else { + str = strstr(term, " name="); + if (str) { + str += 6; + rc = common_path_parser(s, str); + if (rc) + goto err; + term += 7; + } + } + } + if (event_subject) { + // scontext + str = strstr(term, "scontext="); + if (str != NULL) { + str += 9; + term = strchr(str, ' '); + if (term == NULL) { + rc = 7; + goto err; + } + *term = 0; + an.scontext = strdup(str); + *term = ' '; + } + } + + if (event_object) { + // tcontext + str = strstr(term, "tcontext="); + if (str != NULL) { + str += 9; + term = strchr(str, ' '); + if (term == NULL) { + rc = 8; + goto err; + } + *term = 0; + an.tcontext = strdup(str); + *term = ' '; + } + } + + // Now get the class...its at the end, so we do things different + str = strstr(term, "tclass="); + if (str == NULL) { + rc = 9; + goto err; + } + str += 7; + term = strchr(str, ' '); + if (term) + *term = 0; + an.avc_class = strdup(str); + if (term) + *term = ' '; + + if (audit_avc_init(s) == 0) { + alist_append(s->avc, &an); + } else { + rc = 10; + goto err; + } + + return 0; +err: + anode_clear(&an); + return rc; +} + +static int parse_kernel_anom(const lnode *n, search_items *s) +{ + char *str, *ptr, *term = n->message; + + // optionally get loginuid + if (event_loginuid != -2) { + str = strstr(term, "auid="); + if (str == NULL) + return 1; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 2; + if (term) + *term = ' '; + else + term = ptr; + } + + // optionally get uid + if (event_uid != -1) { + str = strstr(term, "uid="); // if promiscuous, we start over + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 3; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 4; + *term = ' '; + } + } + + // optionally get gid + if (event_gid != -1) { + str = strstr(term, "gid="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 5; + *term = 0; + errno = 0; + s->gid = strtoul(ptr, NULL, 10); + if (errno) + return 6; + *term = ' '; + } + } + + if (event_session_id != -2) { + str = strstr(term, "ses="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 7; + if (term) + *term = ' '; + else + term = ptr; + } + } + + if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str) { + str += 5; + term = strchr(str, ' '); + if (term == NULL) + return 8; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 9; + } + } + + // get pid + if (event_pid != -1) { + str = strstr(term, "pid="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 10; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 11; + *term = ' '; + } + } + + if (event_comm) { + // dont do this search unless needed + str = strstr(term, "comm="); + if (str) { + str += 5; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 12; + *term = 0; + s->comm = strdup(str); + *term = '"'; + } else + s->comm = unescape(str); + } + } + + if (n->type == AUDIT_SECCOMP) { + if (event_exe) { + // dont do this search unless needed + str = strstr(n->message, "exe="); + if (str) { + str += 4; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 13; + *term = 0; + s->exe = strdup(str); + *term = '"'; + } else + s->exe = unescape(str); + } else + return 14; + } + + // get arch + str = strstr(term, "arch="); + if (str == NULL) + return 0; // A few kernel versions don't have it + ptr = str + 5; + term = strchr(ptr, ' '); + if (term == NULL) + return 15; + *term = 0; + errno = 0; + s->arch = (int)strtoul(ptr, NULL, 16); + if (errno) + return 16; + *term = ' '; + // get syscall + str = strstr(term, "syscall="); + if (str == NULL) + return 17; + ptr = str + 8; + term = strchr(ptr, ' '); + if (term == NULL) + return 18; + *term = 0; + errno = 0; + s->syscall = (int)strtoul(ptr, NULL, 10); + if (errno) + return 19; + *term = ' '; + } + + return 0; +} + +// This is for messages that only have the loginuid as the item +// of interest. +static int parse_simple_message(const lnode *n, search_items *s) +{ + char *str, *ptr, *term = n->message; + + // optionally get loginuid - old kernels skip auid for CONFIG_CHANGE + if (event_loginuid != -2) { + str = strstr(term, "auid="); + if (str == NULL && n->type != AUDIT_CONFIG_CHANGE) + return 1; + if (str) { + ptr = str + 5; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 2; + if (term) + *term = ' '; + else + term = ptr; + } + } + + // ses + if (event_session_id != -2 ) { + str = strstr(term, "ses="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 3; + if (term) + *term = ' '; + else + term = ptr; + } + } + + // Now get subj label + if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str != NULL) { + str += 5; + term = strchr(str, ' '); + if (term) + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + if (term) + *term = ' '; + else // Set it back to something sane + term = str; + } else + return 4; + } + } + + if (event_key) { + str = strstr(term, "key="); + if (str != NULL) { + if (!s->key) { + //create + s->key = malloc(sizeof(slist)); + if (s->key == NULL) + return 5; + slist_create(s->key); + } + ptr = str + 4; + if (*ptr == '"') { + ptr++; + term = strchr(ptr, '"'); + if (term != NULL) { + *term = 0; + if (s->key) { + // append + snode sn; + sn.str = strdup(ptr); + sn.key = NULL; + sn.hits = 1; + slist_append(s->key, &sn); + } + *term = '"'; + } else + return 6; + } else { + if (s->key) { + char *saved; + char *keyptr = unescape(ptr); + char *kptr = strtok_r(keyptr, + key_sep, &saved); + while (kptr) { + snode sn; + // append + sn.str = strdup(kptr); + sn.key = NULL; + sn.hits = 1; + slist_append(s->key, &sn); + kptr = strtok_r(NULL, + key_sep, &saved); + } + free(keyptr); + } + } + } + } + + // defaulting this to 1 for these messages. The kernel generally + // does not log the res since it can be nothing but success. + // But it can still be overriden below if res= is found in the event + if (n->type == AUDIT_CONFIG_CHANGE) + s->success = S_SUCCESS; + + // and results (usually last) + if (event_success != S_UNSET) { + str = strstr(term, "res="); + if (str != NULL) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->success = strtoul(ptr, NULL, 10); + if (errno) + return 7; + if (term) + *term = ' '; + } + } + + return 0; +} + +static int parse_tty(const lnode *n, search_items *s) +{ + char *str, *ptr, *term=n->message; + + // get pid + if (event_pid != -1) { + str = strstr(n->message, "pid="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 1; + *term = 0; + errno = 0; + s->pid = strtoul(ptr, NULL, 10); + if (errno) + return 2; + *term = ' '; + } + } + + // optionally get uid + if (event_uid != -1) { + str = strstr(term, " uid="); // if promiscuous, we start over + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 3; + *term = 0; + errno = 0; + s->uid = strtoul(ptr, NULL, 10); + if (errno) + return 4; + *term = ' '; + } + } + + // optionally get loginuid + if (event_loginuid != -2) { + str = strstr(term, "auid="); + if (str == NULL) + return 5; + ptr = str + 5; + term = strchr(ptr, ' '); + if (term) + *term = 0; + errno = 0; + s->loginuid = strtoul(ptr, NULL, 10); + if (errno) + return 6; + if (term) + *term = ' '; + else + term = ptr; + } + + // ses + if (event_session_id != -2 ) { + str = strstr(term, "ses="); + if (str) { + ptr = str + 4; + term = strchr(ptr, ' '); + if (term == NULL) + return 7; + *term = 0; + errno = 0; + s->session_id = strtoul(ptr, NULL, 10); + if (errno) + return 8; + *term = ' '; + } + } + +/* if (event_subject) { + // scontext + str = strstr(term, "subj="); + if (str) { + str += 5; + term = strchr(str, ' '); + if (term == NULL) + return 9; + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.scontext = strdup(str); + alist_append(s->avc, &an); + *term = ' '; + } else + return 10; + } + } */ + + if (event_comm) { + // dont do this search unless needed + str = strstr(term, "comm="); + if (str) { + str += 5; + if (*str == '"') { + str++; + term = strchr(str, '"'); + if (term == NULL) + return 11; + *term = 0; + s->comm = strdup(str); + *term = '"'; + } else + s->comm = unescape(str); + } + } + + return 0; +} + +static int parse_pkt(const lnode *n, search_items *s) +{ + char *str, *ptr, *term=n->message; + + // get hostname + if (event_hostname) { + str = strstr(n->message, "saddr="); + if (str) { + ptr = str + 6; + term = strchr(ptr, ' '); + if (term == NULL) + return 1; + *term = 0; + s->hostname = strdup(ptr); + *term = ' '; + } + } + + // obj context + if (event_object) { + str = strstr(term, "obj="); + if (str != NULL) { + str += 4; + term = strchr(str, ' '); + if (term) + *term = 0; + if (audit_avc_init(s) == 0) { + anode an; + + anode_init(&an); + an.tcontext = strdup(str); + alist_append(s->avc, &an); + if (term) + *term = ' '; + } else + return 2; + } + } + + return 0; +} + diff --git a/framework/src/audit/src/ausearch-parse.h b/framework/src/audit/src/ausearch-parse.h new file mode 100644 index 00000000..36557105 --- /dev/null +++ b/framework/src/audit/src/ausearch-parse.h @@ -0,0 +1,33 @@ +/* +* ausearch-parse.h - Header file for ausearch-llist.c +* Copyright (c) 2005 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUPARSE_HEADER +#define AUPARSE_HEADER + +#include "config.h" +#include "ausearch-llist.h" + +int extract_search_items(llist *l); + +#endif + diff --git a/framework/src/audit/src/ausearch-report.c b/framework/src/audit/src/ausearch-report.c new file mode 100644 index 00000000..a4f1e15d --- /dev/null +++ b/framework/src/audit/src/ausearch-report.c @@ -0,0 +1,362 @@ +/* +* ausearch-report.c - Format and output events +* Copyright (c) 2005-09,2011-13 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "config.h" +#include +#include +#include +#include +#include "libaudit.h" +#include "ausearch-options.h" +#include "ausearch-parse.h" +#include "ausearch-lookup.h" +#include "auparse-idata.h" +#include "auparse-defs.h" + +/* Local functions */ +static void output_raw(llist *l); +static void output_default(llist *l); +static void output_interpreted(llist *l); +static void output_interpreted_node(const lnode *n, const event *e); +static void interpret(char *name, char *val, int comma, int rtype); + +/* The machine based on elf type */ +static unsigned long machine = -1; +static int cur_syscall = -1; + +/* The first syscall argument */ +static unsigned long long a0, a1; + +/* This function branches to the correct output format */ +void output_record(llist *l) +{ + switch (report_format) { + case RPT_RAW: + output_raw(l); + break; + case RPT_DEFAULT: + output_default(l); + break; + case RPT_INTERP: + output_interpreted(l); + break; + case RPT_PRETTY: + break; + default: + fprintf(stderr, "Report format error"); + exit(1); + } +} + +/* This function will output the record as is */ +static void output_raw(llist *l) +{ + const lnode *n; + + list_first(l); + n = list_get_cur(l); + if (!n) { + fprintf(stderr, "Error - no elements in record."); + return; + } + do { + puts(n->message); + } while ((n=list_next(l))); +} + +/* + * This function will take the linked list and format it for output. No + * interpretation is performed. The output order is lifo for everything. + */ +static void output_default(llist *l) +{ + const lnode *n; + + list_last(l); + n = list_get_cur(l); + printf("----\ntime->%s", ctime(&l->e.sec)); + if (!n) { + fprintf(stderr, "Error - no elements in record."); + return; + } + if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) + puts(n->message); // No injection possible + else { + do { + safe_print_string_n(n->message, n->mlen, 1); + } while ((n=list_prev(l))); + } +} + +/* + * This function will take the linked list and format it for output. + * Interpretation is performed to aid understanding of records. The output + * order is lifo for everything. + */ +static void output_interpreted(llist *l) +{ + const lnode *n; + + list_last(l); + n = list_get_cur(l); + printf("----\n"); + if (!n) { + fprintf(stderr, "Error - no elements in record."); + return; + } + if (n->type >= AUDIT_DAEMON_START && n->type < AUDIT_SYSCALL) + output_interpreted_node(n, &(l->e)); + else { + do { + output_interpreted_node(n, &(l->e)); + } while ((n=list_prev(l))); + } +} + +/* + * This function will cycle through a single record and lookup each field's + * value that it finds. + */ +static void output_interpreted_node(const lnode *n, const event *e) +{ + char *ptr, *str = n->message; + int found, comma = 0; + int num = n->type; + struct tm *btm; + char tmp[32]; + + // Reset these because each record could be different + machine = -1; + cur_syscall = -1; + + /* Check and see if we start with a node + * If we do, and there is a space in the line + * move the pointer to the first character past + * the space + */ + if (e->node) { + if ((ptr=strchr(str, ' ')) != NULL) { + str = ptr+1; + } + } + + // First locate time stamp. + ptr = strchr(str, '('); + if (ptr == NULL) { + fprintf(stderr, "can't find time stamp\n"); + return; + } + + *ptr++ = 0; /* move to the start of the timestamp */ + + // print everything up to it. + if (num >= 0) { + const char * bptr; + bptr = audit_msg_type_to_name(num); + if (bptr) { + if (e->node) + printf("node=%s ", e->node); + printf("type=%s msg=audit(", bptr); + goto no_print; + } + } + if (e->node) + printf("node=%s ", e->node); + printf("%s(", str); +no_print: + + str = strchr(ptr, ')'); + if(str == NULL) + return; + *str++ = 0; + btm = localtime(&e->sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + printf("%s", tmp); + printf(".%03d:%lu) ", e->milli, e->serial); + + if (n->type == AUDIT_SYSCALL) { + a0 = n->a0; + a1 = n->a1; + } + + // for each item. + found = 0; + while (str && *str && (ptr = strchr(str, '='))) { + char *name, *val; + comma = 0; + found = 1; + + // look back to last space - this is name + name = ptr; + while (*name != ' ' && name > str) + --name; + *ptr++ = 0; + + // print everything up to the '=' + printf("%s=", str); + + // Some user messages have msg='uid=500 in this case + // skip the msg= piece since the real stuff is the uid= + if (strcmp(name, "msg") == 0) { + str = ptr; + continue; + } + + // In the above case, after msg= we need to trim the ' from uid + if (*name == '\'') + name++; + + // get string after = to the next space or end - this is value + if (*ptr == '\'' || *ptr == '"') { + str = strchr(ptr+1, *ptr); + if (str) { + str++; + if (*str) + *str++ = 0; + } + } else { + str = strchr(ptr, ','); + val = strchr(ptr, ' '); + if (str && val && (str < val)) { + // Value side has commas and another field exists + // Known: LABEL_LEVEL_CHANGE banners=none,none + // Known: ROLL_ASSIGN new-role=r,r + // Known: any MAC LABEL can potentially have commas + int ftype = auparse_interp_adjust_type(n->type, + name, val); + if (ftype == AUPARSE_TYPE_MAC_LABEL) { + str = val; + *str++ = 0; + } else { + *str++ = 0; + comma = 1; + } + } else if (str && (val == NULL)) { + // Goes all the way to the end. Done parsing + // Known: MCS context in PATH rec obj=u:r:t:s0:c2,c7 + int ftype = auparse_interp_adjust_type(n->type, + name, ptr); + if (ftype == AUPARSE_TYPE_MAC_LABEL) + str = NULL; + else { + *str++ = 0; + comma = 1; + } + } else if (val) { + // There is another field, point to next (normal path) + str = val; + *str++ = 0; + } + } + // val points to begin & str 1 past end + val = ptr; + + // print interpreted string + interpret(name, val, comma, n->type); + } + // If nothing found, just print out as is + if (!found && ptr == NULL && str) + safe_print_string(str, 1); + + // If last field had comma, output the rest + else if (comma) + safe_print_string(str, 1); + printf("\n"); +} + +static void interpret(char *name, char *val, int comma, int rtype) +{ + int type; + idata id; + + while (*name == ' '||*name == '(') + name++; + + if (*name == 'a' && strcmp(name, "acct") == 0) { + // Remove trailing punctuation + int len = strlen(val); + if (val[len-1] == ':') + val[len-1] = 0; + } + type = auparse_interp_adjust_type(rtype, name, val); + + if (rtype == AUDIT_SYSCALL || rtype == AUDIT_SECCOMP) { + if (machine == (unsigned long)-1) + machine = audit_detect_machine(); + if (*name == 'a' && strcmp(name, "arch") == 0) { + unsigned long ival; + errno = 0; + ival = strtoul(val, NULL, 16); + if (errno) { + printf("arch conversion error(%s) ", val); + return; + } + machine = audit_elf_to_machine(ival); + } + if (cur_syscall < 0 && *name == 's' && + strcmp(name, "syscall") == 0) { + unsigned long ival; + errno = 0; + ival = strtoul(val, NULL, 10); + if (errno) { + printf("syscall conversion error(%s) ", val); + return; + } + cur_syscall = ival; + } + id.syscall = cur_syscall; + } else + id.syscall = 0; + id.machine = machine; + id.a0 = a0; + id.a1 = a1; + id.name = name; + id.val = val; + + char *out = auparse_do_interpretation(type, &id); + if (type == AUPARSE_TYPE_UNCLASSIFIED) + printf("%s%c", val, comma ? ',' : ' '); + else if (name[0] == 'k' && strcmp(name, "key") == 0) { + char *str, *ptr = out; + int count = 0; + while ((str = strchr(ptr, AUDIT_KEY_SEPARATOR))) { + *str = 0; + if (count == 0) { + printf("%s", ptr); + count++; + } else + printf(" key=%s", ptr); + ptr = str+1; + } + if (count == 0) + printf("%s ", out); + else + printf(" key=%s ", ptr); + } else if (type == AUPARSE_TYPE_TTY_DATA) + printf("%s", out); + else + printf("%s ", out); + + free(out); +} + diff --git a/framework/src/audit/src/ausearch-string.c b/framework/src/audit/src/ausearch-string.c new file mode 100644 index 00000000..3c5c55e3 --- /dev/null +++ b/framework/src/audit/src/ausearch-string.c @@ -0,0 +1,178 @@ +/* +* ausearch-string.c - Minimal linked list library for strings +* Copyright (c) 2005,2008,2014 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include "ausearch-string.h" +#include +#include + + +void slist_create(slist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +void slist_last(slist *l) +{ + register snode* cur; + + if (l->head == NULL) + return; + + // Try using cur so that we don't have to start at beginnning + if (l->cur) + cur = l->cur; + else + cur = l->head; + + // Loop until no next value + while (cur->next) + cur = cur->next; + l->cur = cur; +} + +snode *slist_next(slist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +void slist_append(slist *l, snode *node) +{ + snode* newnode; + + newnode = malloc(sizeof(snode)); + + if (node->str) + newnode->str = node->str; + else + newnode->str = NULL; + + if (node->key) + newnode->key = node->key; + else + newnode->key = NULL; + + newnode->hits = node->hits; + newnode->next = NULL; + + // Make sure cursor is at the end + slist_last(l); + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +void slist_clear(slist* l) +{ + snode* nextnode; + register snode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->str); + free(current->key); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +/* This function dominates the timing of aureport. Needs to be more efficient */ +int slist_add_if_uniq(slist *l, const char *str) +{ + snode sn; + register snode *cur; + + cur = l->head; + while (cur) { + if (strcmp(str, cur->str) == 0) { + cur->hits++; + l->cur = cur; + return 0; + } else + cur = cur->next; + } + + /* No matches, append to the end */ + sn.str = strdup(str); + sn.key = NULL; + sn.hits = 1; + slist_append(l, &sn); + return 1; +} + +// If lprev would be NULL, use l->head +static void swap_nodes(snode *lprev, snode *left, snode *right) +{ + snode *t = right->next; + if (lprev) + lprev->next = right; + right->next = left; + left->next = t; +} + +// This will sort the list from most hits to least +void slist_sort_by_hits(slist *l) +{ + register snode* cur, *prev; + + if (l->cnt <= 1) + return; + + prev = cur = l->head; + + while (cur && cur->next) { + /* If the next node is bigger */ + if (cur->hits < cur->next->hits) { + if (cur == l->head) { + // Update the actual list head + l->head = cur->next; + prev = NULL; + } + swap_nodes(prev, cur, cur->next); + + // start over + prev = cur = l->head; + continue; + } + prev = cur; + cur = cur->next; + } + // End with cur pointing at first record + l->cur = l->head; +} + diff --git a/framework/src/audit/src/ausearch-string.h b/framework/src/audit/src/ausearch-string.h new file mode 100644 index 00000000..08cf2ffc --- /dev/null +++ b/framework/src/audit/src/ausearch-string.h @@ -0,0 +1,59 @@ +/* +* ausearch-string.h - Header file for ausearch-string.c +* Copyright (c) 2005,2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AUSTRING_HEADER +#define AUSTRING_HEADER + +#include "config.h" + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _snode{ + char *str; // The string + char *key; // The key string + unsigned int hits; // Number of times this string was attempted to be added + struct _snode* next; // Next string node pointer +} snode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + snode *head; // List head + snode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} slist; + +void slist_create(slist *l); +static inline void slist_first(slist *l) { l->cur = l->head; } +void slist_last(slist *l); +snode *slist_next(slist *l); +static inline snode *slist_get_cur(slist *l) { return l->cur; } +void slist_append(slist *l, snode *node); +void slist_clear(slist* l); + +/* append a string if its not already on the list */ +int slist_add_if_uniq(slist *l, const char *str); +void slist_sort_by_hits(slist *l); + +#endif + diff --git a/framework/src/audit/src/ausearch-time.c b/framework/src/audit/src/ausearch-time.c new file mode 100644 index 00000000..3cd33c87 --- /dev/null +++ b/framework/src/audit/src/ausearch-time.c @@ -0,0 +1,412 @@ +/* ausearch-time.c - time handling utility functions + * Copyright 2006-08,2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include "ausearch-time.h" + + +#define SECONDS_IN_DAY 24*60*60 +static void clear_tm(struct tm *t); +static void replace_time(struct tm *t1, struct tm *t2); +static void replace_date(struct tm *t1, struct tm *t2); + + +time_t start_time = 0, end_time = 0; + +struct nv_pair { + int value; + const char *name; +}; + +static struct nv_pair timetab[] = { + { T_NOW, "now" }, + { T_RECENT, "recent" }, + { T_TODAY, "today" }, + { T_YESTERDAY, "yesterday" }, + { T_THIS_WEEK, "this-week" }, + { T_WEEK_AGO, "week-ago" }, + { T_THIS_MONTH, "this-month" }, + { T_THIS_YEAR, "this-year" }, +}; +#define TIME_NAMES (sizeof(timetab)/sizeof(timetab[0])) + +int lookup_time(const char *name) +{ + int i; + + for (i = 0; i < TIME_NAMES; i++) { + if (strcmp(timetab[i].name, name) == 0) { + return timetab[i].value; + } + } + return -1; + +} + +static void clear_tm(struct tm *t) +{ + t->tm_sec = 0; /* seconds */ + t->tm_min = 0; /* minutes */ + t->tm_hour = 0; /* hours */ + t->tm_mday = 0; /* day of the month */ + t->tm_mon = 0; /* month */ + t->tm_year = 0; /* year */ + t->tm_isdst = 0; /* DST flag */ +} + +static void set_tm_now(struct tm *d) +{ + time_t t = time(NULL); + struct tm *tv = localtime(&t); + replace_time(d, tv); + replace_date(d, tv); +} + +static void set_tm_today(struct tm *d) +{ + time_t t = time(NULL); + struct tm *tv = localtime(&t); + d->tm_sec = 0; /* seconds */ + d->tm_min = 0; /* minutes */ + d->tm_hour = 0; /* hours */ + replace_date(d, tv); +} + +static void set_tm_yesterday(struct tm *d) +{ + time_t t = time(NULL) - (time_t)(SECONDS_IN_DAY); + struct tm *tv = localtime(&t); + d->tm_sec = 0; /* seconds */ + d->tm_min = 0; /* minutes */ + d->tm_hour = 0; /* hours */ + replace_date(d, tv); +} + +static void set_tm_recent(struct tm *d) +{ + time_t t = time(NULL) - (time_t)(10*60); /* 10 minutes ago */ + struct tm *tv = localtime(&t); + replace_time(d, tv); + replace_date(d, tv); +} + +static void set_tm_this_week(struct tm *d) +{ + time_t t = time(NULL); + struct tm *tv = localtime(&t); + d->tm_sec = 0; /* seconds */ + d->tm_min = 0; /* minutes */ + d->tm_hour = 0; /* hours */ + t -= (time_t)(tv->tm_wday*SECONDS_IN_DAY); + tv = localtime(&t); + replace_date(d, tv); +} + +static void set_tm_week_ago(struct tm *d) +{ + time_t t = time(NULL); + struct tm *tv; + d->tm_sec = 0; /* seconds */ + d->tm_min = 0; /* minutes */ + d->tm_hour = 0; /* hours */ + t -= (time_t)(7*SECONDS_IN_DAY); + tv = localtime(&t); + replace_date(d, tv); +} + +static void set_tm_this_month(struct tm *d) +{ + time_t t = time(NULL); + struct tm *tv = localtime(&t); + d->tm_sec = 0; /* seconds */ + d->tm_min = 0; /* minutes */ + d->tm_hour = 0; /* hours */ + replace_date(d, tv); + d->tm_mday = 1; /* override day of month */ +} + +static void set_tm_this_year(struct tm *d) +{ + time_t t = time(NULL); + struct tm *tv = localtime(&t); + d->tm_sec = 0; /* seconds */ + d->tm_min = 0; /* minutes */ + d->tm_hour = 0; /* hours */ + replace_date(d, tv); + d->tm_mday = 1; /* override day of month */ + d->tm_mon = 0; /* override month */ +} + +/* Combine date & time into 1 struct. Results in date. */ +static void add_tm(struct tm *d, struct tm *t) +{ + time_t dst; + struct tm *lt; + + replace_time(d, t); + + /* Now we need to figure out if DST is in effect */ + dst = time(NULL); + lt = localtime(&dst); + d->tm_isdst = lt->tm_isdst; +} + +/* The time in t1 is replaced by t2 */ +static void replace_time(struct tm *t1, struct tm *t2) +{ + t1->tm_sec = t2->tm_sec; /* seconds */ + t1->tm_min = t2->tm_min; /* minutes */ + t1->tm_hour = t2->tm_hour; /* hours */ +} + +/* The date in t1 is replaced by t2 */ +static void replace_date(struct tm *t1, struct tm *t2) +{ + t1->tm_mday = t2->tm_mday; /* day */ + t1->tm_mon = t2->tm_mon; /* month */ + t1->tm_year = t2->tm_year; /* year */ + t1->tm_isdst = t2->tm_isdst; /* daylight savings time */ +} + +/* Given 2 char strings, create a time struct * +void set_time(struct tm *t, int num, const char *t1, const char *t2) +{ + switch (num) + { + case 1: + // if keyword, init time + // elif time use today and replace time + // elif date, set to 00:00:01 and replace date + // else error + break; + case 2: + // if keyword + // init time with it + // get other time str and replace + // otherwise, figure out which is time + // and set time adding them + break; + default: + break; + } +} */ + +static int lookup_and_set_time(const char *da, struct tm *d) +{ + int retval = lookup_time(da); + if (retval >= 0) { + switch (retval) + { + case T_NOW: + set_tm_now(d); + break; + case T_RECENT: + set_tm_recent(d); + break; + case T_TODAY: + set_tm_today(d); + break; + case T_YESTERDAY: + set_tm_yesterday(d); + break; + case T_THIS_WEEK: + set_tm_this_week(d); + break; + case T_WEEK_AGO: + set_tm_week_ago(d); + break; + case T_THIS_MONTH: + set_tm_this_month(d); + break; + case T_THIS_YEAR: + set_tm_this_year(d); + break; + } + return 0; + } else + return -1; +} + +/* static void print_time(struct tm *d) +{ + char outstr[200]; + strftime(outstr, sizeof(outstr), "%c", d); + printf("%s\n", outstr); +} */ + +int ausearch_time_start(const char *da, const char *ti) +{ +/* If da == NULL, use current date */ +/* If ti == NULL, then use midnight 00:00:00 */ + int rc = 0; + struct tm d, t; + char *ret; + + if (da == NULL) + set_tm_now(&d); + else { + if (lookup_and_set_time(da, &d) < 0) { + ret = strptime(da, "%x", &d); + if (ret == NULL) { + fprintf(stderr, + "Invalid start date (%s). Month, Day, and Year are required.\n", + da); + return 1; + } + if (*ret != 0) { + fprintf(stderr, + "Error parsing start date (%s)\n", da); + return 1; + } + } else { + int keyword=lookup_time(da); + if (keyword == T_RECENT || keyword == T_NOW) { + if (ti == NULL || strcmp(ti, "00:00:00") == 0) + goto set_it; + } + } + } + + if (ti != NULL) { + char tmp_t[36]; + + if (strlen(ti) <= 5) { + snprintf(tmp_t, sizeof(tmp_t), "%s:00", ti); + } else { + tmp_t[0]=0; + strncat(tmp_t, ti, sizeof(tmp_t)-1); + } + ret = strptime(tmp_t, "%X", &t); + if (ret == NULL) { + fprintf(stderr, + "Invalid start time (%s). Hour, Minute, and Second are required.\n", + ti); + return 1; + } + if (*ret != 0) { + fprintf(stderr, "Error parsing start time (%s)\n", ti); + return 1; + } + } else + clear_tm(&t); + + add_tm(&d, &t); + if (d.tm_year < 104) { + fprintf(stderr, "Error - year is %d\n", d.tm_year+1900); + return -1; + } +set_it: + // printf("start is: %s\n", ctime(&start_time)); + start_time = mktime(&d); + if (start_time == -1) { + fprintf(stderr, "Error converting start time\n"); + rc = -1; + } + return rc; +} + +int ausearch_time_end(const char *da, const char *ti) +{ +/* If date == NULL, use current date */ +/* If ti == NULL, use current time */ + int rc = 0; + struct tm d, t; + char *ret; + + if (da == NULL) + set_tm_now(&d); + else { + if (lookup_and_set_time(da, &d) < 0) { + ret = strptime(da, "%x", &d); + if (ret == NULL) { + fprintf(stderr, + "Invalid end date (%s). Month, Day, and Year are required.\n", + da); + return 1; + } + if (*ret != 0) { + fprintf(stderr, + "Error parsing end date (%s)\n", da); + return 1; + } + } else { + int keyword=lookup_time(da); + if (keyword == T_RECENT || keyword == T_NOW) { + if (ti == NULL || strcmp(ti, "00:00:00") == 0) + goto set_it; + } + // Special case today + if (keyword == T_TODAY) { + set_tm_now(&d); + if (ti == NULL || strcmp(ti, "00:00:00") == 0) + goto set_it; + } + } + } + + if (ti != NULL) { + char tmp_t[36]; + + if (strlen(ti) <= 5) { + snprintf(tmp_t, sizeof(tmp_t), "%s:00", ti); + } else { + tmp_t[0]=0; + strncat(tmp_t, ti, sizeof(tmp_t)-1); + } + ret = strptime(tmp_t, "%X", &t); + if (ret == NULL) { + fprintf(stderr, + "Invalid end time (%s). Hour, Minute, and Second are required.\n", + ti); + return 1; + } + if (*ret != 0) { + fprintf(stderr, "Error parsing end time (%s)\n", ti); + return 1; + } + } else { + time_t tt = time(NULL); + struct tm *tv = localtime(&tt); + clear_tm(&t); + t.tm_hour = tv->tm_hour; + t.tm_min = tv->tm_min; + t.tm_sec = tv->tm_sec; + t.tm_isdst = tv->tm_isdst; + + } + add_tm(&d, &t); + if (d.tm_year < 104) { + fprintf(stderr, "Error - year is %d\n", d.tm_year+1900); + return -1; + } +set_it: + // printf("end is: %s\n", ctime(&end_time)); + end_time = mktime(&d); + if (end_time == -1) { + fprintf(stderr, "Error converting end time\n"); + rc = -1; + } + return rc; +} + diff --git a/framework/src/audit/src/ausearch-time.h b/framework/src/audit/src/ausearch-time.h new file mode 100644 index 00000000..15051a5a --- /dev/null +++ b/framework/src/audit/src/ausearch-time.h @@ -0,0 +1,38 @@ +/* ausearch-time.h - header file for ausearch-time.c + * Copyright 2006-07 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#ifndef AUSEARCH_TIME_HEADERS +#define AUSEARCH_TIME_HEADERS + +#include + +enum { T_NOW, T_RECENT, T_TODAY, T_YESTERDAY, T_THIS_WEEK, T_WEEK_AGO, + T_THIS_MONTH, T_THIS_YEAR }; + +extern time_t start_time, end_time; + +int lookup_time(const char *name); +int ausearch_time_start(const char *da, const char *ti); +int ausearch_time_end(const char *da, const char *ti); + +#endif + diff --git a/framework/src/audit/src/ausearch.c b/framework/src/audit/src/ausearch.c new file mode 100644 index 00000000..06213f8b --- /dev/null +++ b/framework/src/audit/src/ausearch.c @@ -0,0 +1,594 @@ +/* + * ausearch.c - main file for ausearch utility + * Copyright 2005-08,2010,2013,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "auditd-config.h" +#include "ausearch-options.h" +#include "ausearch-lol.h" +#include "ausearch-lookup.h" +#include "auparse.h" +#include "ausearch-checkpt.h" + + +static FILE *log_fd = NULL; +static lol lo; +static int found = 0; +static int input_is_pipe = 0; +static int timeout_interval = 3; /* timeout in seconds */ +static int files_to_process = 0; /* number of log files yet to process when reading multiple */ +static int process_logs(void); +static int process_log_fd(void); +static int process_stdin(void); +static int process_file(char *filename); +static int get_record(llist **); + +extern const char *checkpt_filename; /* checkpoint file name */ +extern int checkpt_timeonly; /* use timestamp from within checkpoint file */ +static int have_chkpt_data = 0; /* have checkpt need to compare wit */ +extern char *user_file; +extern int force_logs; +extern int match(llist *l); +extern void output_record(llist *l); + +static int userfile_is_dir = 0; + +static int is_pipe(int fd) +{ + struct stat st; + int pipe_mode=0; + + if (fstat(fd, &st) == 0) { + if (S_ISFIFO(st.st_mode)) + pipe_mode = 1; + } + return pipe_mode; +} + +int main(int argc, char *argv[]) +{ + struct rlimit limit; + int rc; + struct stat sb; + + /* Check params and build regexpr */ + setlocale (LC_ALL, ""); + if (check_params(argc, argv)) + return 1; + + /* Raise the rlimits in case we're being started from a shell + * with restrictions. Not a fatal error. */ + limit.rlim_cur = RLIM_INFINITY; + limit.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_FSIZE, &limit); + setrlimit(RLIMIT_CPU, &limit); + set_aumessage_mode(MSG_STDERR, DBG_NO); + (void) umask( umask( 077 ) | 027 ); + + /* Load the checkpoint file if requested */ + if (checkpt_filename) { + rc = load_ChkPt(checkpt_filename); + /* + * If < -1, then some load/parse error + * If == -1 then no file present (OK) + * If == 0, then checkpoint has data + */ + if (rc < -1) { + (void)free((void *)checkpt_filename); + free_ChkPtMemory(); + return 10; /* bad checkpoint status file */ + } else if (rc == -1) { + /* + * No file, so no checking required. This just means + * we have never checkpointed before and this is the + * first time. + */ + have_chkpt_data = 0; + } else { + /* We will need to check */ + have_chkpt_data++; + } + } + + lol_create(&lo); + if (user_file) { + if (stat(user_file, &sb) == -1) { + perror("stat"); + return 1; + } + switch (sb.st_mode & S_IFMT) { + case S_IFDIR: + userfile_is_dir = 1; + rc = process_logs(); + break; + case S_IFREG: + default: + rc = process_file(user_file); + break; + } + } else if (force_logs) + rc = process_logs(); + else if (is_pipe(0)) + rc = process_stdin(); + else + rc = process_logs(); + + /* Generate a checkpoint if required */ + if (checkpt_filename) { + /* Providing we found something and haven't failed */ + if (!checkpt_failure && found) + save_ChkPt(checkpt_filename); + free_ChkPtMemory(); + free((void *)checkpt_filename); + /* + * A checkpoint failure at this point means either + * - we failed in attempting to create the checkpouint file + * and so we will return 11 + * - we had a corrupted checkpoint file and so we will return 12 + */ + if (checkpt_failure) { + rc = ((checkpt_failure & CP_CORRUPTED) == + CP_CORRUPTED) ? 12 : 11; + } + } + + lol_clear(&lo); + ilist_clear(event_type); + free(event_type); + free(user_file); + free((char *)event_key); + auparse_destroy(NULL); + if (rc) + return rc; + if (!found) { + if (report_format != RPT_RAW) + fprintf(stderr, "\n"); + return 1; + } + return 0; +} + +static int process_logs(void) +{ + struct daemon_conf config; + char *filename; + int len, num = 0; + int found_chkpt_file = -1; + int ret; + + if (user_file && userfile_is_dir) { + char dirname[MAXPATHLEN]; + clear_config (&config); + + strcpy(dirname, user_file); + if (dirname[strlen(dirname)-1] != '/') + strcat(dirname, "/"); + strcat (dirname, "audit.log"); + free((void *)config.log_file); + config.log_file=strdup(dirname); + fprintf(stderr, "NOTE - using logs in %s\n", config.log_file); + } + else { + /* Load config so we know where logs are */ + if (load_config(&config, TEST_SEARCH)) { + fprintf(stderr, + "NOTE - using built-in logs: %s\n", + config.log_file); + } + } + + /* for each file */ + len = strlen(config.log_file) + 16; + filename = malloc(len); + if (!filename) { + fprintf(stderr, "No memory\n"); + free_config(&config); + return 1; + } + /* Find oldest log file */ + snprintf(filename, len, "%s", config.log_file); + do { + if (access(filename, R_OK) != 0) + break; + + /* + * If we have prior checkpoint data, we ignore files till we + * find the file we last checkpointed from + */ + if (checkpt_filename && have_chkpt_data) { + struct stat sbuf; + + if (stat(filename, &sbuf)) { + fprintf(stderr, "Error stat'ing %s (%s)\n", + filename, strerror(errno)); + free(filename); + free_config(&config); + return 1; + } + /* + * Have we accessed the checkpointed file? + * If so, stop checking further files. + */ + if ( (sbuf.st_dev == chkpt_input_dev) && + (sbuf.st_ino == chkpt_input_ino) ) { + /* + * If we are ignoing all but time, then we + * don't stop checking more files and just + * let this loop go to completion and hence + * we will find the 'oldest' file. + */ + if (!checkpt_timeonly) { + found_chkpt_file = num++; + break; + } + } + } + + num++; + snprintf(filename, len, "%s.%d", config.log_file, num); + } while (1); + + /* If a checkpoint is loaded but can't find it's file, and we + * are not only just checking the timestamp from the checkpoint file, + * we need to error */ + if (checkpt_filename && have_chkpt_data && found_chkpt_file == -1 + && !checkpt_timeonly) { + free(filename); + free_config(&config); + return 10; + } + + num--; + + /* We note how many files we need to process */ + files_to_process = num; + + /* Got it, now process logs from last to first */ + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else + snprintf(filename, len, "%s", config.log_file); + do { + if ((ret = process_file(filename))) { + free(filename); + free_config(&config); + return ret; + } + if (just_one && found) + break; + files_to_process--; /* one less file to process */ + + /* Get next log file */ + num--; + if (num > 0) + snprintf(filename, len, "%s.%d", config.log_file, num); + else if (num == 0) + snprintf(filename, len, "%s", config.log_file); + else + break; + } while (1); + /* + * If performing a checkpoint, set the checkpointed + * file details - ie remember the last file processed + */ + ret = 0; + if (checkpt_filename) + ret = set_ChkPtFileDetails(filename); + + free(filename); + free_config(&config); + return ret; +} + +/* + * Decide if we should start outputing events given we loaded a checkpoint. + * + * The previous checkpoint will have recorded the last event outputted, + * if there was one. If nothing is to be output, either the audit.log file + * is empty, all the events in it were incomplete, or ??? + * + * We can return + * 0 no output + * 1 can output + * 2 can output but not this event + * 3 we have found an event whose time is > MAX_EVENT_DELTA_SECS secs + * past our checkpoint time, which means this particulare event is + * complete. This should not happen, for we should have found our + * checkpoint event before ANY other completed event. + * + */ +static int chkpt_output_decision(event * e) +{ + static int can_output = 0; + + /* Short cut. Once we made the decision, it's made for good */ + if (can_output) + return 1; + + /* If there was no checkpoint file, we turn on output */ + if (have_chkpt_data == 0) { + can_output = 1; + return 1; /* can output on this event */ + } + + /* + * If the previous checkpoint had no recorded output, then + * we assume everything was partial so we turn on output + */ + if (chkpt_input_levent.sec == 0) { + can_output = 1; + return 1; /* can output on this event */ + } + + /* + * If we are ignoring all but event time from within the checkpoint + * file, then we output if the current event's time is greater than + * or equal to the checkpoint time. + */ + if (checkpt_timeonly) { + if ( (chkpt_input_levent.sec < e->sec) || + ( (chkpt_input_levent.sec == e->sec) && + (chkpt_input_levent.milli <= e->milli) ) ) { + can_output = 1; + return 1; /* can output on this event */ + } + } + + if (chkpt_input_levent.sec == e->sec && + chkpt_input_levent.milli == e->milli && + chkpt_input_levent.serial == e->serial && + chkpt_input_levent.type == e->type ) { + + /* So far a match, so now check the nodes */ + if (chkpt_input_levent.node == NULL && e->node == NULL) { + can_output = 1; + return 2; /* output after this event */ + } + if (chkpt_input_levent.node && e->node && + (strcmp(chkpt_input_levent.node, e->node) == 0) ) { + can_output = 1; + return 2; /* output after this event */ + } + /* + * The nodes are different. Drop through to further checks. + */ + } + /* + * If the event we are looking at is more than MAX_EVENT_DELTA_SECS + * seconds past our checkpoint event, then by definition we should + * have had a complete event (ie a complete event is one where at + * least MAX_EVENT_DELTA_SECS seconds have passed since it's last + * output record). + * This means there is a problem, for the recorded checkpoint event was + * the last complete event in the file when we last processed it. + * Normally we see this if the checkpoint is very old and the system + * has used the same inode again in an audit log file. + */ + if ( (chkpt_input_levent.sec < e->sec) && + ((e->sec - chkpt_input_levent.sec) > MAX_EVENT_DELTA_SECS) ) { +/* fprintf(stderr, "%s %lu.%03u:%lu vs %s %lu.%03u:%lu\n", + chkpt_input_levent.host ? chkpt_input_levent.host : "-", + chkpt_input_levent.sec, chkpt_input_levent.milli, + chkpt_input_levent.serial, + e->host, e->sec, e->milli, e->serial); */ + return 3; + } + + return 0; +} + +static int process_log_fd(void) +{ + llist *entries; // entries in a record + int ret; + int do_output = 1; + + /* For each record in file */ + do { + ret = get_record(&entries); + if ((ret != 0)||(entries->cnt == 0)) { + break; + } + /* + * We flush all events on the last log file being processed. + * Thus incomplete events are 'carried forward' to be + * completed from the rest of it's records we expect to find + * in the next file we are about to process. + */ + if (match(entries)) { + /* + * If we are checkpointing, decide if we output + * this event + */ + if (checkpt_filename) + do_output = chkpt_output_decision(&entries->e); + + if (do_output == 1) { + found = 1; + output_record(entries); + + /* Remember this event if checkpointing */ + if (checkpt_filename) { + if (set_ChkPtLastEvent(&entries->e)) { + list_clear(entries); + free(entries); + fclose(log_fd); + return 4; /* no memory */ + } + } + } else if (do_output == 3) { + fprintf(stderr, + "Corrupted checkpoint file. Inode match, but newer complete event (%lu.%03u:%lu) found before loaded checkpoint %lu.%03u:%lu\n", + entries->e.sec, entries->e.milli, + entries->e.serial, + chkpt_input_levent.sec, + chkpt_input_levent.milli, + chkpt_input_levent.serial); + checkpt_failure |= CP_CORRUPTED; + list_clear(entries); + free(entries); + fclose(log_fd); + return 10; + } + if (just_one) { + list_clear(entries); + free(entries); + break; + } + if (line_buffered) + fflush(stdout); + } + list_clear(entries); + free(entries); + } while (ret == 0); + fclose(log_fd); + + return 0; +} + +static void alarm_handler(int signal) +{ + /* will interrupt current syscall */ +} + +static int process_stdin(void) +{ + log_fd = stdin; + input_is_pipe=1; + + if (signal(SIGALRM, alarm_handler) == SIG_ERR || + siginterrupt(SIGALRM, 1) == -1) + return -1; + + return process_log_fd(); +} + +static int process_file(char *filename) +{ + log_fd = fopen(filename, "rm"); + if (log_fd == NULL) { + fprintf(stderr, "Error opening %s (%s)\n", filename, + strerror(errno)); + return 1; + } + + __fsetlocking(log_fd, FSETLOCKING_BYCALLER); + return process_log_fd(); +} + +/* + * This function returns a malloc'd buffer of the next record in the audit + * logs. It returns 0 on success, 1 on eof, -1 on error. + */ +static int get_record(llist **l) +{ + char *rc; + char *buff = NULL; + int rcount = 0, timer_running = 0; + + /* + * If we have any events ready to print ie have all records that + * make up the event, we just return. If not, we read more lines + * from the files until we get a complete event or finish reading + * input + */ + *l = get_ready_event(&lo); + if (*l) + return 0; + + while (1) { + rcount++; + + if (!buff) { + buff = malloc(MAX_AUDIT_MESSAGE_LENGTH); + if (!buff) + return -1; + } + + if (input_is_pipe && rcount > 1) { + timer_running = 1; + alarm(timeout_interval); + } + + rc = fgets_unlocked(buff, MAX_AUDIT_MESSAGE_LENGTH, + log_fd); + + if (timer_running) { + /* timer may have fired but thats ok */ + timer_running = 0; + alarm(0); + } + + if (rc) { + if (lol_add_record(&lo, buff)) { + *l = get_ready_event(&lo); + if (*l) + break; + } + } else { + free(buff); + /* + * If we get an EINTR error or we are at EOF, we check + * to see if we have any events to print and return + * appropriately. If we are the last file being + * processed, we mark all incomplete events as + * complete so they will be printed. + */ + if ((ferror_unlocked(log_fd) && + errno == EINTR) || feof_unlocked(log_fd)) { + /* + * Only mark all events as L_COMPLETE if we are + * the last file being processed. + * We DO NOT do this if we are checkpointing. + */ + if (files_to_process == 0) { + if (!checkpt_filename) + terminate_all_events(&lo); + } + *l = get_ready_event(&lo); + if (*l) + return 0; + else + return 1; + } else + return -1; /* all other errors are terminal */ + } + } + free(buff); + return 0; +} + diff --git a/framework/src/audit/src/autrace.c b/framework/src/audit/src/autrace.c new file mode 100644 index 00000000..03c0b556 --- /dev/null +++ b/framework/src/audit/src/autrace.c @@ -0,0 +1,329 @@ +/* autrace.c -- + * Copyright 2005-09,2011,2015 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "private.h" + +/* + * This program will add the audit rules to trace a process similar + * to strace. It will then execute the process. + */ +static int threat = 0; +static int count_rules(void); +static int count_em(int fd); +extern int delete_all_rules(int fd); + +static void usage(void) +{ + fprintf(stderr, "usage: autrace [-r] program\n"); +} + +static int insert_rule(int audit_fd, const char *field) +{ + int rc; + int flags = AUDIT_FILTER_EXIT; + int action = AUDIT_ALWAYS; + struct audit_rule_data *rule = malloc(sizeof(struct audit_rule_data)); + int machine = audit_detect_machine(); + char *t_field = NULL; + + if (rule == NULL) + goto err; + memset(rule, 0, sizeof(struct audit_rule_data)); + if (threat) { + rc = 0; + if (machine != MACH_AARCH64) { + rc |= audit_rule_syscallbyname_data(rule, "open"); + rc |= audit_rule_syscallbyname_data(rule, "creat"); + rc |= audit_rule_syscallbyname_data(rule, "rename"); + rc |= audit_rule_syscallbyname_data(rule, "unlink"); + rc |= audit_rule_syscallbyname_data(rule, "mknod"); + rc |= audit_rule_syscallbyname_data(rule, "mkdir"); + rc |= audit_rule_syscallbyname_data(rule, "rmdir"); + rc |= audit_rule_syscallbyname_data(rule, "chown"); + rc |= audit_rule_syscallbyname_data(rule, "lchown"); + rc |= audit_rule_syscallbyname_data(rule, "chmod"); + rc |= audit_rule_syscallbyname_data(rule, "link"); + rc |= audit_rule_syscallbyname_data(rule, "symlink"); + rc |= audit_rule_syscallbyname_data(rule, "readlink"); + } + rc |= audit_rule_syscallbyname_data(rule, "openat"); + rc |= audit_rule_syscallbyname_data(rule, "truncate"); + rc |= audit_rule_syscallbyname_data(rule, "renameat"); + rc |= audit_rule_syscallbyname_data(rule, "unlinkat"); + rc |= audit_rule_syscallbyname_data(rule, "mknodat"); + rc |= audit_rule_syscallbyname_data(rule, "mkdirat"); + rc |= audit_rule_syscallbyname_data(rule, "chdir"); + rc |= audit_rule_syscallbyname_data(rule, "fchownat"); + rc |= audit_rule_syscallbyname_data(rule, "fchmodat"); + rc |= audit_rule_syscallbyname_data(rule, "linkat"); + rc |= audit_rule_syscallbyname_data(rule, "symlinkat"); + rc |= audit_rule_syscallbyname_data(rule, "readlinkat"); + rc |= audit_rule_syscallbyname_data(rule, "execve"); + rc |= audit_rule_syscallbyname_data(rule, "name_to_handle_at"); + + if (machine != MACH_X86 && machine != MACH_S390X && + machine != MACH_S390) { + rc |= audit_rule_syscallbyname_data(rule, "connect"); + rc |= audit_rule_syscallbyname_data(rule, "bind"); + rc |= audit_rule_syscallbyname_data(rule, "accept"); + rc |= audit_rule_syscallbyname_data(rule, "sendto"); + rc |= audit_rule_syscallbyname_data(rule, "recvfrom"); + rc |= audit_rule_syscallbyname_data(rule, "accept4"); + } + + rc |= audit_rule_syscallbyname_data(rule, "sendfile"); + } else + rc = audit_rule_syscallbyname_data(rule, "all"); + if (rc < 0) + goto err; + t_field = strdup(field); + rc = audit_rule_fieldpair_data(&rule, t_field, flags); + free(t_field); + if (rc < 0) + goto err; + rc = audit_add_rule_data(audit_fd, rule, flags, action); + if (rc < 0) + goto err; + + // Now if i386, lets add its network rules + if (machine == MACH_X86 || machine == MACH_S390X || + machine == MACH_S390) { + int i, a0[6] = { SYS_CONNECT, SYS_BIND, SYS_ACCEPT, SYS_SENDTO, + SYS_RECVFROM, SYS_ACCEPT4 }; + for (i=0; i<6; i++) { + char pair[32]; + + memset(rule, 0, sizeof(struct audit_rule_data)); + rc |= audit_rule_syscallbyname_data(rule, "socketcall"); + snprintf(pair, sizeof(pair), "a0=%d", a0[i]); + rc |= audit_rule_fieldpair_data(&rule, pair, flags); + t_field = strdup(field); + rc |= audit_rule_fieldpair_data(&rule, t_field, flags); + free(t_field); + rc |= audit_add_rule_data(audit_fd, rule, flags, action); + } + } + free(rule); + return 0; +err: + fprintf(stderr, "Error inserting audit rule for %s\n", field); + free(rule); + return 1; +} + +int key_match(struct audit_reply *rep) +{ + return 1; +} + +/* + * Algorithm: + * check that user is root + * check to see if program exists + * if so fork, child waits for parent + * parent clears audit rules, loads audit all syscalls with child's pid + * parent tells child to go & waits for sigchld + * child exec's program + * parent deletes rules after getting sigchld + */ +int main(int argc, char *argv[]) +{ + int fd[2]; + int pid,cmd=1; + char buf[2]; + + if (argc < 2) { + usage(); + return 1; + } + if (strcmp(argv[cmd], "-h") == 0) { + usage(); + return 1; + } + if (strcmp(argv[cmd], "-r") == 0) { + threat = 1; + cmd++; + } + if (getuid() != 0) { + fprintf(stderr, "You must be root to run this program.\n"); + return 1; + } + if (access(argv[cmd], X_OK)) { + if (errno == ENOENT) + fprintf(stderr, "Error - can't find: %s\n", argv[cmd]); + else + fprintf(stderr, "Error checking %s (%s)\n", + argv[cmd], strerror(errno)); + return 1; + } + set_aumessage_mode(MSG_STDERR, DBG_NO); + switch (count_rules()) + { + case -1: + if (errno == ECONNREFUSED) + fprintf(stderr, + "The audit system is disabled\n"); + else + fprintf(stderr, + "Error - can't get rule count.\n"); + return 1; + case 0: + break; + default: + fprintf(stderr, + "autrace cannot be run with rules loaded.\n" + "Please delete all rules using 'auditctl -D' if you " + "really wanted to\nrun this command.\n"); + return 1; + } + if (pipe(fd) != 0) { + fprintf(stderr, "Error creating pipe.\n"); + return 1; + } + + switch ((pid=fork())) + { + case -1: + fprintf(stderr, "Error forking.\n"); + return 1; + case 0: /* Child */ + close(fd[1]); + printf("Waiting to execute: %s\n", argv[cmd]); + while (read(fd[0], buf, 1) == -1 && errno == EINTR) + /* blank */ ; + close(fd[0]); + execvp(argv[cmd], &argv[cmd]); + fprintf(stderr, "Failed to exec %s\n", argv[cmd]); + return 1; + default: /* Parent */ + close(fd[0]); + fcntl(fd[1], F_SETFD, FD_CLOEXEC); + { + char field[16]; + int audit_fd; + audit_fd = audit_open(); + if (audit_fd < 0) + exit(1); + snprintf(field, sizeof(field), "pid=%d", pid); + if (insert_rule(audit_fd, field)) { + kill(pid,SIGTERM); + (void)delete_all_rules(audit_fd); + exit(1); + } + snprintf(field, sizeof(field), "ppid=%d", pid); + if (insert_rule(audit_fd, field)) { + kill(pid,SIGTERM); + (void)delete_all_rules(audit_fd); + exit(1); + } + sleep(1); + if (write(fd[1],"1", 1) != 1) { + kill(pid,SIGTERM); + (void)delete_all_rules(audit_fd); + exit(1); + } + waitpid(pid, NULL, 0); + close(fd[1]); + puts("Cleaning up..."); + (void)delete_all_rules(audit_fd); + close(audit_fd); + } + printf("Trace complete. " + "You can locate the records with " + "\'ausearch -i -p %d\'\n", + pid); + break; + } + + return 0; +} + +static int count_rules(void) +{ + int fd, total, rc; + + fd = audit_open(); + if (fd < 0) + return -1; + + rc = audit_request_rules_list_data(fd); + if (rc > 0) + total = count_em(fd); + else + total = -1; + + close(fd); + return total; +} + +static int count_em(int fd) +{ + int i, retval, count = 0; + int timeout = 40; /* loop has delay of .1 - this is 4 seconds */ + struct audit_reply rep; + fd_set read_mask; + + FD_ZERO(&read_mask); + FD_SET(fd, &read_mask); + + for (i = 0; i < timeout; i++) { + retval = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + if (retval > 0) { + struct timeval t; + + if (rep.type == NLMSG_ERROR && + rep.error->error == 0) + continue; + t.tv_sec = 0; + t.tv_usec = 100000; /* .1 second */ + do { + retval=select(fd+1, &read_mask, NULL, NULL, &t); + } while (retval < 0 && errno == EINTR); + switch (rep.type) + { + case NLMSG_DONE: + return count; + case AUDIT_LIST_RULES: + i = 0; + count++; + break; + case NLMSG_ERROR: + return -1; + default: + break; + } + } + } + return count; +} + diff --git a/framework/src/audit/src/delete_all.c b/framework/src/audit/src/delete_all.c new file mode 100644 index 00000000..4e0feed1 --- /dev/null +++ b/framework/src/audit/src/delete_all.c @@ -0,0 +1,109 @@ +/* delete_all.c -- + * Copyright 2005-06, 2008-09,2014 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ +#include "config.h" +#include +#include +#include + +#include "libaudit.h" +#include "private.h" + +#include "auditctl-llist.h" + +extern int key_match(const struct audit_rule_data *r); + +/* Returns 0 for success and -1 for failure */ +int delete_all_rules(int fd) +{ + int seq, i, rc; + int timeout = 40; /* tenths of seconds */ + struct audit_reply rep; + fd_set read_mask; + llist l; + lnode *n; + + /* list the rules */ + seq = audit_request_rules_list_data(fd); + if (seq <= 0) + return -1; + + FD_ZERO(&read_mask); + FD_SET(fd, &read_mask); + list_create(&l); + + for (i = 0; i < timeout; i++) { + struct timeval t; + + t.tv_sec = 0; + t.tv_usec = 100000; /* .1 second */ + do { + rc = select(fd+1, &read_mask, NULL, NULL, &t); + } while (rc < 0 && errno == EINTR); + // We'll try to read just in case + rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + if (rc > 0) { + /* Reset timeout */ + i = 0; + + /* Don't make decisions based on wrong packet */ + if (rep.nlh->nlmsg_seq != seq) + continue; + + /* If we get done or error, break out */ + if (rep.type == NLMSG_DONE) + break; + + if (rep.type == NLMSG_ERROR && rep.error->error) { + audit_msg(LOG_ERR, + "Error receiving rules list (%s)", + strerror(-rep.error->error)); + return -1; + } + + /* If its not what we are expecting, keep looping */ + if (rep.type != AUDIT_LIST_RULES) + continue; + + if (key_match(rep.ruledata)) + list_append(&l, rep.ruledata, + sizeof(struct audit_rule_data) + + rep.ruledata->buflen); + + } + } + list_first(&l); + n = l.cur; + while (n) { + /* Bounce it right back with delete */ + rc = audit_send(fd, AUDIT_DEL_RULE, n->r, n->size); + if (rc < 0) { + audit_msg(LOG_ERR, "Error deleting rule (%s)", + strerror(-rc)); + return -1; + } + n = list_next(&l); + } + list_clear(&l); + + return 0; +} + diff --git a/framework/src/audit/src/libev/Makefile.am b/framework/src/audit/src/libev/Makefile.am new file mode 100644 index 00000000..a35e7b0d --- /dev/null +++ b/framework/src/audit/src/libev/Makefile.am @@ -0,0 +1,29 @@ +# Makefile.am-- +# Copyright 2008,2011-12 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# +VERSION_INFO = 4:0:0 +EXTRA_DIST = README ev_epoll.c ev_poll.c ev_select.c libev.m4 +AM_CFLAGS = -fPIC -DPIC -g -fno-strict-aliasing ${DEBUG} + +noinst_HEADERS = ev.h ev_vars.h ev_wrap.h event.h +noinst_LIBRARIES = libev.a + +libev_a_SOURCES = ev.c event.c diff --git a/framework/src/audit/src/libev/README b/framework/src/audit/src/libev/README new file mode 100644 index 00000000..31f61938 --- /dev/null +++ b/framework/src/audit/src/libev/README @@ -0,0 +1,58 @@ +libev is a high-performance event loop/event model with lots of features. +(see benchmark at http://libev.schmorp.de/bench.html) + + +ABOUT + + Homepage: http://software.schmorp.de/pkg/libev + Mailinglist: libev@lists.schmorp.de + http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev + Library Documentation: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod + + Libev is modelled (very losely) after libevent and the Event perl + module, but is faster, scales better and is more correct, and also more + featureful. And also smaller. Yay. + + Some of the specialties of libev not commonly found elsewhere are: + + - extensive and detailed, readable documentation (not doxygen garbage). + - fully supports fork, can detect fork in various ways and automatically + re-arms kernel mechanisms that do not support fork. + - highly optimised select, poll, epoll, kqueue and event ports backends. + - filesystem object (path) watching (with optional linux inotify support). + - wallclock-based times (using absolute time, cron-like). + - relative timers/timeouts (handle time jumps). + - fast intra-thread communication between multiple + event loops (with optional fast linux eventfd backend). + - extremely easy to embed (fully documented, no dependencies, + autoconf supported but optional). + - very small codebase, no bloated library, simple code. + - fully extensible by being able to plug into the event loop, + integrate other event loops, integrate other event loop users. + - very little memory use (small watchers, small event loop data). + - optional C++ interface allowing method and function callbacks + at no extra memory or runtime overhead. + - optional Perl interface with similar characteristics (capable + of running Glib/Gtk2 on libev). + - support for other languages (multiple C++ interfaces, D, Ruby, + Python) available from third-parties. + + Examples of programs that embed libev: the EV perl module, node.js, + auditd, rxvt-unicode, gvpe (GNU Virtual Private Ethernet), the + Deliantra MMORPG server (http://www.deliantra.net/), Rubinius (a + next-generation Ruby VM), the Ebb web server, the Rev event toolkit. + + +CONTRIBUTORS + + libev was written and designed by Marc Lehmann and Emanuele Giaquinta. + + The following people sent in patches or made other noteworthy + contributions to the design (for minor patches, see the Changes + file. If I forgot to include you, please shout at me, it was an + accident): + + W.C.A. Wijngaards + Christopher Layne + Chris Brody + diff --git a/framework/src/audit/src/libev/ev.c b/framework/src/audit/src/libev/ev.c new file mode 100644 index 00000000..3e7013f9 --- /dev/null +++ b/framework/src/audit/src/libev/ev.c @@ -0,0 +1,4971 @@ +/* + * libev event processing core, watcher management + * + * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +/* this big block deduces configuration from config.h */ +#ifndef EV_STANDALONE +# ifdef EV_CONFIG_H +# include EV_CONFIG_H +# else +# include "config.h" +# endif + +# if HAVE_FLOOR +# ifndef EV_USE_FLOOR +# define EV_USE_FLOOR 1 +# endif +# endif + +# if HAVE_CLOCK_SYSCALL +# ifndef EV_USE_CLOCK_SYSCALL +# define EV_USE_CLOCK_SYSCALL 1 +# ifndef EV_USE_REALTIME +# define EV_USE_REALTIME 0 +# endif +# ifndef EV_USE_MONOTONIC +# define EV_USE_MONOTONIC 1 +# endif +# endif +# elif !defined EV_USE_CLOCK_SYSCALL +# define EV_USE_CLOCK_SYSCALL 0 +# endif + +# if HAVE_CLOCK_GETTIME +# ifndef EV_USE_MONOTONIC +# define EV_USE_MONOTONIC 1 +# endif +# ifndef EV_USE_REALTIME +# define EV_USE_REALTIME 0 +# endif +# else +# ifndef EV_USE_MONOTONIC +# define EV_USE_MONOTONIC 0 +# endif +# ifndef EV_USE_REALTIME +# define EV_USE_REALTIME 0 +# endif +# endif + +# if HAVE_NANOSLEEP +# ifndef EV_USE_NANOSLEEP +# define EV_USE_NANOSLEEP EV_FEATURE_OS +# endif +# else +# undef EV_USE_NANOSLEEP +# define EV_USE_NANOSLEEP 0 +# endif + +# if HAVE_SELECT && HAVE_SYS_SELECT_H +# ifndef EV_USE_SELECT +# define EV_USE_SELECT EV_FEATURE_BACKENDS +# endif +# else +# undef EV_USE_SELECT +# define EV_USE_SELECT 0 +# endif + +# if HAVE_POLL && HAVE_POLL_H +# ifndef EV_USE_POLL +# define EV_USE_POLL EV_FEATURE_BACKENDS +# endif +# else +# undef EV_USE_POLL +# define EV_USE_POLL 0 +# endif + +# if HAVE_EPOLL_CTL && HAVE_SYS_EPOLL_H +# ifndef EV_USE_EPOLL +# define EV_USE_EPOLL EV_FEATURE_BACKENDS +# endif +# else +# undef EV_USE_EPOLL +# define EV_USE_EPOLL 0 +# endif + +# if HAVE_KQUEUE && HAVE_SYS_EVENT_H +# ifndef EV_USE_KQUEUE +# define EV_USE_KQUEUE EV_FEATURE_BACKENDS +# endif +# else +# undef EV_USE_KQUEUE +# define EV_USE_KQUEUE 0 +# endif + +# if HAVE_PORT_H && HAVE_PORT_CREATE +# ifndef EV_USE_PORT +# define EV_USE_PORT EV_FEATURE_BACKENDS +# endif +# else +# undef EV_USE_PORT +# define EV_USE_PORT 0 +# endif + +# if HAVE_INOTIFY_INIT && HAVE_SYS_INOTIFY_H +# ifndef EV_USE_INOTIFY +# define EV_USE_INOTIFY EV_FEATURE_OS +# endif +# else +# undef EV_USE_INOTIFY +# define EV_USE_INOTIFY 0 +# endif + +# if HAVE_SIGNALFD && HAVE_SYS_SIGNALFD_H +# ifndef EV_USE_SIGNALFD +# define EV_USE_SIGNALFD EV_FEATURE_OS +# endif +# else +# undef EV_USE_SIGNALFD +# define EV_USE_SIGNALFD 0 +# endif + +# if HAVE_EVENTFD +# ifndef EV_USE_EVENTFD +# define EV_USE_EVENTFD EV_FEATURE_OS +# endif +# else +# undef EV_USE_EVENTFD +# define EV_USE_EVENTFD 0 +# endif + +#endif + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef EV_H +# include EV_H +#else +# include "ev.h" +#endif + +#if EV_NO_THREADS +# undef EV_NO_SMP +# define EV_NO_SMP 1 +# undef ECB_NO_THREADS +# define ECB_NO_THREADS 1 +#endif +#if EV_NO_SMP +# undef EV_NO_SMP +# define ECB_NO_SMP 1 +#endif + +#ifndef _WIN32 +# include +# include +# include +#else +# include +# define WIN32_LEAN_AND_MEAN +# include +# include +# ifndef EV_SELECT_IS_WINSOCKET +# define EV_SELECT_IS_WINSOCKET 1 +# endif +# undef EV_AVOID_STDIO +#endif + +/* OS X, in its infinite idiocy, actually HARDCODES + * a limit of 1024 into their select. Where people have brains, + * OS X engineers apparently have a vacuum. Or maybe they were + * ordered to have a vacuum, or they do anything for money. + * This might help. Or not. + */ +#define _DARWIN_UNLIMITED_SELECT 1 + +/* this block tries to deduce configuration from header-defined symbols and defaults */ + +/* try to deduce the maximum number of signals on this platform */ +#if defined EV_NSIG +/* use what's provided */ +#elif defined NSIG +# define EV_NSIG (NSIG) +#elif defined _NSIG +# define EV_NSIG (_NSIG) +#elif defined SIGMAX +# define EV_NSIG (SIGMAX+1) +#elif defined SIG_MAX +# define EV_NSIG (SIG_MAX+1) +#elif defined _SIG_MAX +# define EV_NSIG (_SIG_MAX+1) +#elif defined MAXSIG +# define EV_NSIG (MAXSIG+1) +#elif defined MAX_SIG +# define EV_NSIG (MAX_SIG+1) +#elif defined SIGARRAYSIZE +# define EV_NSIG (SIGARRAYSIZE) /* Assume ary[SIGARRAYSIZE] */ +#elif defined _sys_nsig +# define EV_NSIG (_sys_nsig) /* Solaris 2.5 */ +#else +# define EV_NSIG (8 * sizeof (sigset_t) + 1) +#endif + +#ifndef EV_USE_FLOOR +# define EV_USE_FLOOR 0 +#endif + +#ifndef EV_USE_CLOCK_SYSCALL +# if __linux && __GLIBC__ == 2 && __GLIBC_MINOR__ < 17 +# define EV_USE_CLOCK_SYSCALL EV_FEATURE_OS +# else +# define EV_USE_CLOCK_SYSCALL 0 +# endif +#endif + +#if !(_POSIX_TIMERS > 0) +# ifndef EV_USE_MONOTONIC +# define EV_USE_MONOTONIC 0 +# endif +# ifndef EV_USE_REALTIME +# define EV_USE_REALTIME 0 +# endif +#endif + +#ifndef EV_USE_MONOTONIC +# if defined _POSIX_MONOTONIC_CLOCK && _POSIX_MONOTONIC_CLOCK >= 0 +# define EV_USE_MONOTONIC EV_FEATURE_OS +# else +# define EV_USE_MONOTONIC 0 +# endif +#endif + +#ifndef EV_USE_REALTIME +# define EV_USE_REALTIME !EV_USE_CLOCK_SYSCALL +#endif + +#ifndef EV_USE_NANOSLEEP +# if _POSIX_C_SOURCE >= 199309L +# define EV_USE_NANOSLEEP EV_FEATURE_OS +# else +# define EV_USE_NANOSLEEP 0 +# endif +#endif + +#ifndef EV_USE_SELECT +# define EV_USE_SELECT EV_FEATURE_BACKENDS +#endif + +#ifndef EV_USE_POLL +# ifdef _WIN32 +# define EV_USE_POLL 0 +# else +# define EV_USE_POLL EV_FEATURE_BACKENDS +# endif +#endif + +#ifndef EV_USE_EPOLL +# if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 4)) +# define EV_USE_EPOLL EV_FEATURE_BACKENDS +# else +# define EV_USE_EPOLL 0 +# endif +#endif + +#ifndef EV_USE_KQUEUE +# define EV_USE_KQUEUE 0 +#endif + +#ifndef EV_USE_PORT +# define EV_USE_PORT 0 +#endif + +#ifndef EV_USE_INOTIFY +# if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 4)) +# define EV_USE_INOTIFY EV_FEATURE_OS +# else +# define EV_USE_INOTIFY 0 +# endif +#endif + +#ifndef EV_PID_HASHSIZE +# define EV_PID_HASHSIZE EV_FEATURE_DATA ? 16 : 1 +#endif + +#ifndef EV_INOTIFY_HASHSIZE +# define EV_INOTIFY_HASHSIZE EV_FEATURE_DATA ? 16 : 1 +#endif + +#ifndef EV_USE_EVENTFD +# if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 7)) +# define EV_USE_EVENTFD EV_FEATURE_OS +# else +# define EV_USE_EVENTFD 0 +# endif +#endif + +#ifndef EV_USE_SIGNALFD +# if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 7)) +# define EV_USE_SIGNALFD EV_FEATURE_OS +# else +# define EV_USE_SIGNALFD 0 +# endif +#endif + +#if 0 /* debugging */ +# define EV_VERIFY 3 +# define EV_USE_4HEAP 1 +# define EV_HEAP_CACHE_AT 1 +#endif + +#ifndef EV_VERIFY +# define EV_VERIFY (EV_FEATURE_API ? 1 : 0) +#endif + +#ifndef EV_USE_4HEAP +# define EV_USE_4HEAP EV_FEATURE_DATA +#endif + +#ifndef EV_HEAP_CACHE_AT +# define EV_HEAP_CACHE_AT EV_FEATURE_DATA +#endif + +#ifdef ANDROID +/* supposedly, android doesn't typedef fd_mask */ +# undef EV_USE_SELECT +# define EV_USE_SELECT 0 +/* supposedly, we need to include syscall.h, not sys/syscall.h, so just disable */ +# undef EV_USE_CLOCK_SYSCALL +# define EV_USE_CLOCK_SYSCALL 0 +#endif + +/* aix's poll.h seems to cause lots of trouble */ +#ifdef _AIX +/* AIX has a completely broken poll.h header */ +# undef EV_USE_POLL +# define EV_USE_POLL 0 +#endif + +/* on linux, we can use a (slow) syscall to avoid a dependency on pthread, */ +/* which makes programs even slower. might work on other unices, too. */ +#if EV_USE_CLOCK_SYSCALL +# include +# ifdef SYS_clock_gettime +# define clock_gettime(id, ts) syscall (SYS_clock_gettime, (id), (ts)) +# undef EV_USE_MONOTONIC +# define EV_USE_MONOTONIC 1 +# else +# undef EV_USE_CLOCK_SYSCALL +# define EV_USE_CLOCK_SYSCALL 0 +# endif +#endif + +/* this block fixes any misconfiguration where we know we run into trouble otherwise */ + +#ifndef CLOCK_MONOTONIC +# undef EV_USE_MONOTONIC +# define EV_USE_MONOTONIC 0 +#endif + +#ifndef CLOCK_REALTIME +# undef EV_USE_REALTIME +# define EV_USE_REALTIME 0 +#endif + +#if !EV_STAT_ENABLE +# undef EV_USE_INOTIFY +# define EV_USE_INOTIFY 0 +#endif + +#if !EV_USE_NANOSLEEP +/* hp-ux has it in sys/time.h, which we unconditionally include above */ +# if !defined _WIN32 && !defined __hpux +# include +# endif +#endif + +#if EV_USE_INOTIFY +# include +# include +/* some very old inotify.h headers don't have IN_DONT_FOLLOW */ +# ifndef IN_DONT_FOLLOW +# undef EV_USE_INOTIFY +# define EV_USE_INOTIFY 0 +# endif +#endif + +#if EV_USE_EVENTFD +/* our minimum requirement is glibc 2.7 which has the stub, but not the header */ +# include +# ifndef EFD_NONBLOCK +# define EFD_NONBLOCK O_NONBLOCK +# endif +# ifndef EFD_CLOEXEC +# ifdef O_CLOEXEC +# define EFD_CLOEXEC O_CLOEXEC +# else +# define EFD_CLOEXEC 02000000 +# endif +# endif +EV_CPP(extern "C") int (eventfd) (unsigned int initval, int flags); +#endif + +#if EV_USE_SIGNALFD +/* our minimum requirement is glibc 2.7 which has the stub, but not the header */ +# include +# ifndef SFD_NONBLOCK +# define SFD_NONBLOCK O_NONBLOCK +# endif +# ifndef SFD_CLOEXEC +# ifdef O_CLOEXEC +# define SFD_CLOEXEC O_CLOEXEC +# else +# define SFD_CLOEXEC 02000000 +# endif +# endif +EV_CPP (extern "C") int signalfd (int fd, const sigset_t *mask, int flags); + +struct signalfd_siginfo +{ + uint32_t ssi_signo; + char pad[128 - sizeof (uint32_t)]; +}; +#endif + +/**/ + +#if EV_VERIFY >= 3 +# define EV_FREQUENT_CHECK ev_verify (EV_A) +#else +# define EV_FREQUENT_CHECK do { } while (0) +#endif + +/* + * This is used to work around floating point rounding problems. + * This value is good at least till the year 4000. + */ +#define MIN_INTERVAL 0.0001220703125 /* 1/2**13, good till 4000 */ +/*#define MIN_INTERVAL 0.00000095367431640625 * 1/2**20, good till 2200 */ + +#define MIN_TIMEJUMP 1. /* minimum timejump that gets detected (if monotonic clock available) */ +#define MAX_BLOCKTIME 59.743 /* never wait longer than this time (to detect time jumps) */ + +#define EV_TV_SET(tv,t) do { tv.tv_sec = (long)t; tv.tv_usec = (long)((t - tv.tv_sec) * 1e6); } while (0) +#define EV_TS_SET(ts,t) do { ts.tv_sec = (long)t; ts.tv_nsec = (long)((t - ts.tv_sec) * 1e9); } while (0) + +/* the following is ecb.h embedded into libev - use update_ev_c to update from an external copy */ +/* ECB.H BEGIN */ +/* + * libecb - http://software.schmorp.de/pkg/libecb + * + * Copyright (©) 2009-2015 Marc Alexander Lehmann + * Copyright (©) 2011 Emanuele Giaquinta + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#ifndef ECB_H +#define ECB_H + +/* 16 bits major, 16 bits minor */ +#define ECB_VERSION 0x00010004 + +#ifdef _WIN32 + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef signed short int16_t; + typedef unsigned short uint16_t; + typedef signed int int32_t; + typedef unsigned int uint32_t; + #if __GNUC__ + typedef signed long long int64_t; + typedef unsigned long long uint64_t; + #else /* _MSC_VER || __BORLANDC__ */ + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #endif + #ifdef _WIN64 + #define ECB_PTRSIZE 8 + typedef uint64_t uintptr_t; + typedef int64_t intptr_t; + #else + #define ECB_PTRSIZE 4 + typedef uint32_t uintptr_t; + typedef int32_t intptr_t; + #endif +#else + #include + #if UINTMAX_MAX > 0xffffffffU + #define ECB_PTRSIZE 8 + #else + #define ECB_PTRSIZE 4 + #endif +#endif + +#define ECB_GCC_AMD64 (__amd64 || __amd64__ || __x86_64 || __x86_64__) +#define ECB_MSVC_AMD64 (_M_AMD64 || _M_X64) + +/* work around x32 idiocy by defining proper macros */ +#if ECB_GCC_AMD64 || ECB_MSVC_AMD64 + #if _ILP32 + #define ECB_AMD64_X32 1 + #else + #define ECB_AMD64 1 + #endif +#endif + +/* many compilers define _GNUC_ to some versions but then only implement + * what their idiot authors think are the "more important" extensions, + * causing enormous grief in return for some better fake benchmark numbers. + * or so. + * we try to detect these and simply assume they are not gcc - if they have + * an issue with that they should have done it right in the first place. + */ +#if !defined __GNUC_MINOR__ || defined __INTEL_COMPILER || defined __SUNPRO_C || defined __SUNPRO_CC || defined __llvm__ || defined __clang__ + #define ECB_GCC_VERSION(major,minor) 0 +#else + #define ECB_GCC_VERSION(major,minor) (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +#endif + +#define ECB_CLANG_VERSION(major,minor) (__clang_major__ > (major) || (__clang_major__ == (major) && __clang_minor__ >= (minor))) + +#if __clang__ && defined __has_builtin + #define ECB_CLANG_BUILTIN(x) __has_builtin (x) +#else + #define ECB_CLANG_BUILTIN(x) 0 +#endif + +#if __clang__ && defined __has_extension + #define ECB_CLANG_EXTENSION(x) __has_extension (x) +#else + #define ECB_CLANG_EXTENSION(x) 0 +#endif + +#define ECB_CPP (__cplusplus+0) +#define ECB_CPP11 (__cplusplus >= 201103L) + +#if ECB_CPP + #define ECB_C 0 + #define ECB_STDC_VERSION 0 +#else + #define ECB_C 1 + #define ECB_STDC_VERSION __STDC_VERSION__ +#endif + +#define ECB_C99 (ECB_STDC_VERSION >= 199901L) +#define ECB_C11 (ECB_STDC_VERSION >= 201112L) + +#if ECB_CPP + #define ECB_EXTERN_C extern "C" + #define ECB_EXTERN_C_BEG ECB_EXTERN_C { + #define ECB_EXTERN_C_END } +#else + #define ECB_EXTERN_C extern + #define ECB_EXTERN_C_BEG + #define ECB_EXTERN_C_END +#endif + +/*****************************************************************************/ + +/* ECB_NO_THREADS - ecb is not used by multiple threads, ever */ +/* ECB_NO_SMP - ecb might be used in multiple threads, but only on a single cpu */ + +#if ECB_NO_THREADS + #define ECB_NO_SMP 1 +#endif + +#if ECB_NO_SMP + #define ECB_MEMORY_FENCE do { } while (0) +#endif + +/* http://www-01.ibm.com/support/knowledgecenter/SSGH3R_13.1.0/com.ibm.xlcpp131.aix.doc/compiler_ref/compiler_builtins.html */ +#if __xlC__ && ECB_CPP + #include +#endif + +#ifndef ECB_MEMORY_FENCE + #if ECB_GCC_VERSION(2,5) || defined __INTEL_COMPILER || (__llvm__ && __GNUC__) || __SUNPRO_C >= 0x5110 || __SUNPRO_CC >= 0x5110 + #if __i386 || __i386__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("lock; orb $0, -1(%%esp)" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("" : : : "memory") + #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("") + #elif ECB_GCC_AMD64 + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mfence" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("" : : : "memory") + #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("") + #elif __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("sync" : : : "memory") + #elif defined __ARM_ARCH_6__ || defined __ARM_ARCH_6J__ \ + || defined __ARM_ARCH_6K__ || defined __ARM_ARCH_6ZK__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mcr p15,0,%0,c7,c10,5" : : "r" (0) : "memory") + #elif defined __ARM_ARCH_7__ || defined __ARM_ARCH_7A__ \ + || defined __ARM_ARCH_7M__ || defined __ARM_ARCH_7R__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb" : : : "memory") + #elif __aarch64__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb ish" : : : "memory") + #elif (__sparc || __sparc__) && !__sparcv8 + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad | #StoreStore | #StoreLoad" : : : "memory") + #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad" : : : "memory") + #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("membar #LoadStore | #StoreStore") + #elif defined __s390__ || defined __s390x__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("bcr 15,0" : : : "memory") + #elif defined __mips__ + /* GNU/Linux emulates sync on mips1 architectures, so we force its use */ + /* anybody else who still uses mips1 is supposed to send in their version, with detection code. */ + #define ECB_MEMORY_FENCE __asm__ __volatile__ (".set mips2; sync; .set mips0" : : : "memory") + #elif defined __alpha__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mb" : : : "memory") + #elif defined __hppa__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("" : : : "memory") + #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("") + #elif defined __ia64__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mf" : : : "memory") + #elif defined __m68k__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("" : : : "memory") + #elif defined __m88k__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("tb1 0,%%r0,128" : : : "memory") + #elif defined __sh__ + #define ECB_MEMORY_FENCE __asm__ __volatile__ ("" : : : "memory") + #endif + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + #if ECB_GCC_VERSION(4,7) + /* see comment below (stdatomic.h) about the C11 memory model. */ + #define ECB_MEMORY_FENCE __atomic_thread_fence (__ATOMIC_SEQ_CST) + #define ECB_MEMORY_FENCE_ACQUIRE __atomic_thread_fence (__ATOMIC_ACQUIRE) + #define ECB_MEMORY_FENCE_RELEASE __atomic_thread_fence (__ATOMIC_RELEASE) + + #elif ECB_CLANG_EXTENSION(c_atomic) + /* see comment below (stdatomic.h) about the C11 memory model. */ + #define ECB_MEMORY_FENCE __c11_atomic_thread_fence (__ATOMIC_SEQ_CST) + #define ECB_MEMORY_FENCE_ACQUIRE __c11_atomic_thread_fence (__ATOMIC_ACQUIRE) + #define ECB_MEMORY_FENCE_RELEASE __c11_atomic_thread_fence (__ATOMIC_RELEASE) + + #elif ECB_GCC_VERSION(4,4) || defined __INTEL_COMPILER || defined __clang__ + #define ECB_MEMORY_FENCE __sync_synchronize () + #elif _MSC_VER >= 1500 /* VC++ 2008 */ + /* apparently, microsoft broke all the memory barrier stuff in Visual Studio 2008... */ + #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) + #define ECB_MEMORY_FENCE _ReadWriteBarrier (); MemoryBarrier() + #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier (); MemoryBarrier() /* according to msdn, _ReadBarrier is not a load fence */ + #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier (); MemoryBarrier() + #elif _MSC_VER >= 1400 /* VC++ 2005 */ + #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) + #define ECB_MEMORY_FENCE _ReadWriteBarrier () + #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier () /* according to msdn, _ReadBarrier is not a load fence */ + #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier () + #elif defined _WIN32 + #include + #define ECB_MEMORY_FENCE MemoryBarrier () /* actually just xchg on x86... scary */ + #elif __SUNPRO_C >= 0x5110 || __SUNPRO_CC >= 0x5110 + #include + #define ECB_MEMORY_FENCE __machine_rw_barrier () + #define ECB_MEMORY_FENCE_ACQUIRE __machine_r_barrier () + #define ECB_MEMORY_FENCE_RELEASE __machine_w_barrier () + #elif __xlC__ + #define ECB_MEMORY_FENCE __sync () + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + #if ECB_C11 && !defined __STDC_NO_ATOMICS__ + /* we assume that these memory fences work on all variables/all memory accesses, */ + /* not just C11 atomics and atomic accesses */ + #include + /* Unfortunately, neither gcc 4.7 nor clang 3.1 generate any instructions for */ + /* any fence other than seq_cst, which isn't very efficient for us. */ + /* Why that is, we don't know - either the C11 memory model is quite useless */ + /* for most usages, or gcc and clang have a bug */ + /* I *currently* lean towards the latter, and inefficiently implement */ + /* all three of ecb's fences as a seq_cst fence */ + /* Update, gcc-4.8 generates mfence for all c++ fences, but nothing */ + /* for all __atomic_thread_fence's except seq_cst */ + #define ECB_MEMORY_FENCE atomic_thread_fence (memory_order_seq_cst) + #endif +#endif + +#ifndef ECB_MEMORY_FENCE + #if !ECB_AVOID_PTHREADS + /* + * if you get undefined symbol references to pthread_mutex_lock, + * or failure to find pthread.h, then you should implement + * the ECB_MEMORY_FENCE operations for your cpu/compiler + * OR provide pthread.h and link against the posix thread library + * of your system. + */ + #include + #define ECB_NEEDS_PTHREADS 1 + #define ECB_MEMORY_FENCE_NEEDS_PTHREADS 1 + + static pthread_mutex_t ecb_mf_lock = PTHREAD_MUTEX_INITIALIZER; + #define ECB_MEMORY_FENCE do { pthread_mutex_lock (&ecb_mf_lock); pthread_mutex_unlock (&ecb_mf_lock); } while (0) + #endif +#endif + +#if !defined ECB_MEMORY_FENCE_ACQUIRE && defined ECB_MEMORY_FENCE + #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE +#endif + +#if !defined ECB_MEMORY_FENCE_RELEASE && defined ECB_MEMORY_FENCE + #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE +#endif + +/*****************************************************************************/ + +#if ECB_CPP + #define ecb_inline static inline +#elif ECB_GCC_VERSION(2,5) + #define ecb_inline static __inline__ +#elif ECB_C99 + #define ecb_inline static inline +#else + #define ecb_inline static +#endif + +#if ECB_GCC_VERSION(3,3) + #define ecb_restrict __restrict__ +#elif ECB_C99 + #define ecb_restrict restrict +#else + #define ecb_restrict +#endif + +typedef int ecb_bool; + +#define ECB_CONCAT_(a, b) a ## b +#define ECB_CONCAT(a, b) ECB_CONCAT_(a, b) +#define ECB_STRINGIFY_(a) # a +#define ECB_STRINGIFY(a) ECB_STRINGIFY_(a) +#define ECB_STRINGIFY_EXPR(expr) ((expr), ECB_STRINGIFY_ (expr)) + +#define ecb_function_ ecb_inline + +#if ECB_GCC_VERSION(3,1) || ECB_CLANG_VERSION(2,8) + #define ecb_attribute(attrlist) __attribute__ (attrlist) +#else + #define ecb_attribute(attrlist) +#endif + +#if ECB_GCC_VERSION(3,1) || ECB_CLANG_BUILTIN(__builtin_constant_p) + #define ecb_is_constant(expr) __builtin_constant_p (expr) +#else + /* possible C11 impl for integral types + typedef struct ecb_is_constant_struct ecb_is_constant_struct; + #define ecb_is_constant(expr) _Generic ((1 ? (struct ecb_is_constant_struct *)0 : (void *)((expr) - (expr)), ecb_is_constant_struct *: 0, default: 1)) */ + + #define ecb_is_constant(expr) 0 +#endif + +#if ECB_GCC_VERSION(3,1) || ECB_CLANG_BUILTIN(__builtin_expect) + #define ecb_expect(expr,value) __builtin_expect ((expr),(value)) +#else + #define ecb_expect(expr,value) (expr) +#endif + +#if ECB_GCC_VERSION(3,1) || ECB_CLANG_BUILTIN(__builtin_prefetch) + #define ecb_prefetch(addr,rw,locality) __builtin_prefetch (addr, rw, locality) +#else + #define ecb_prefetch(addr,rw,locality) +#endif + +/* no emulation for ecb_decltype */ +#if ECB_CPP11 + // older implementations might have problems with decltype(x)::type, work around it + template struct ecb_decltype_t { typedef T type; }; + #define ecb_decltype(x) ecb_decltype_t::type +#elif ECB_GCC_VERSION(3,0) || ECB_CLANG_VERSION(2,8) + #define ecb_decltype(x) __typeof__ (x) +#endif + +#if _MSC_VER >= 1300 + #define ecb_deprecated __declspec (deprecated) +#else + #define ecb_deprecated ecb_attribute ((__deprecated__)) +#endif + +#if _MSC_VER >= 1500 + #define ecb_deprecated_message(msg) __declspec (deprecated (msg)) +#elif ECB_GCC_VERSION(4,5) + #define ecb_deprecated_message(msg) ecb_attribute ((__deprecated__ (msg)) +#else + #define ecb_deprecated_message(msg) ecb_deprecated +#endif + +#if _MSC_VER >= 1400 + #define ecb_noinline __declspec (noinline) +#else + #define ecb_noinline ecb_attribute ((__noinline__)) +#endif + +#define ecb_unused ecb_attribute ((__unused__)) +#define ecb_const ecb_attribute ((__const__)) +#define ecb_pure ecb_attribute ((__pure__)) + +#if ECB_C11 || __IBMC_NORETURN + /* http://www-01.ibm.com/support/knowledgecenter/SSGH3R_13.1.0/com.ibm.xlcpp131.aix.doc/language_ref/noreturn.html */ + #define ecb_noreturn _Noreturn +#elif ECB_CPP11 + #define ecb_noreturn [[noreturn]] +#elif _MSC_VER >= 1200 + /* http://msdn.microsoft.com/en-us/library/k6ktzx3s.aspx */ + #define ecb_noreturn __declspec (noreturn) +#else + #define ecb_noreturn ecb_attribute ((__noreturn__)) +#endif + +#if ECB_GCC_VERSION(4,3) + #define ecb_artificial ecb_attribute ((__artificial__)) + #define ecb_hot ecb_attribute ((__hot__)) + #define ecb_cold ecb_attribute ((__cold__)) +#else + #define ecb_artificial + #define ecb_hot + #define ecb_cold +#endif + +/* put around conditional expressions if you are very sure that the */ +/* expression is mostly true or mostly false. note that these return */ +/* booleans, not the expression. */ +#define ecb_expect_false(expr) ecb_expect (!!(expr), 0) +#define ecb_expect_true(expr) ecb_expect (!!(expr), 1) +/* for compatibility to the rest of the world */ +#define ecb_likely(expr) ecb_expect_true (expr) +#define ecb_unlikely(expr) ecb_expect_false (expr) + +/* count trailing zero bits and count # of one bits */ +#if ECB_GCC_VERSION(3,4) \ + || (ECB_CLANG_BUILTIN(__builtin_clz) && ECB_CLANG_BUILTIN(__builtin_clzll) \ + && ECB_CLANG_BUILTIN(__builtin_ctz) && ECB_CLANG_BUILTIN(__builtin_ctzll) \ + && ECB_CLANG_BUILTIN(__builtin_popcount)) + /* we assume int == 32 bit, long == 32 or 64 bit and long long == 64 bit */ + #define ecb_ld32(x) (__builtin_clz (x) ^ 31) + #define ecb_ld64(x) (__builtin_clzll (x) ^ 63) + #define ecb_ctz32(x) __builtin_ctz (x) + #define ecb_ctz64(x) __builtin_ctzll (x) + #define ecb_popcount32(x) __builtin_popcount (x) + /* no popcountll */ +#else + ecb_function_ ecb_const int ecb_ctz32 (uint32_t x); + ecb_function_ ecb_const int + ecb_ctz32 (uint32_t x) + { + int r = 0; + + x &= ~x + 1; /* this isolates the lowest bit */ + +#if ECB_branchless_on_i386 + r += !!(x & 0xaaaaaaaa) << 0; + r += !!(x & 0xcccccccc) << 1; + r += !!(x & 0xf0f0f0f0) << 2; + r += !!(x & 0xff00ff00) << 3; + r += !!(x & 0xffff0000) << 4; +#else + if (x & 0xaaaaaaaa) r += 1; + if (x & 0xcccccccc) r += 2; + if (x & 0xf0f0f0f0) r += 4; + if (x & 0xff00ff00) r += 8; + if (x & 0xffff0000) r += 16; +#endif + + return r; + } + + ecb_function_ ecb_const int ecb_ctz64 (uint64_t x); + ecb_function_ ecb_const int + ecb_ctz64 (uint64_t x) + { + int shift = x & 0xffffffffU ? 0 : 32; + return ecb_ctz32 (x >> shift) + shift; + } + + ecb_function_ ecb_const int ecb_popcount32 (uint32_t x); + ecb_function_ ecb_const int + ecb_popcount32 (uint32_t x) + { + x -= (x >> 1) & 0x55555555; + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + x = ((x >> 4) + x) & 0x0f0f0f0f; + x *= 0x01010101; + + return x >> 24; + } + + ecb_function_ ecb_const int ecb_ld32 (uint32_t x); + ecb_function_ ecb_const int ecb_ld32 (uint32_t x) + { + int r = 0; + + if (x >> 16) { x >>= 16; r += 16; } + if (x >> 8) { x >>= 8; r += 8; } + if (x >> 4) { x >>= 4; r += 4; } + if (x >> 2) { x >>= 2; r += 2; } + if (x >> 1) { r += 1; } + + return r; + } + + ecb_function_ ecb_const int ecb_ld64 (uint64_t x); + ecb_function_ ecb_const int ecb_ld64 (uint64_t x) + { + int r = 0; + + if (x >> 32) { x >>= 32; r += 32; } + + return r + ecb_ld32 (x); + } +#endif + +ecb_function_ ecb_const ecb_bool ecb_is_pot32 (uint32_t x); +ecb_function_ ecb_const ecb_bool ecb_is_pot32 (uint32_t x) { return !(x & (x - 1)); } +ecb_function_ ecb_const ecb_bool ecb_is_pot64 (uint64_t x); +ecb_function_ ecb_const ecb_bool ecb_is_pot64 (uint64_t x) { return !(x & (x - 1)); } + +ecb_function_ ecb_const uint8_t ecb_bitrev8 (uint8_t x); +ecb_function_ ecb_const uint8_t ecb_bitrev8 (uint8_t x) +{ + return ( (x * 0x0802U & 0x22110U) + | (x * 0x8020U & 0x88440U)) * 0x10101U >> 16; +} + +ecb_function_ ecb_const uint16_t ecb_bitrev16 (uint16_t x); +ecb_function_ ecb_const uint16_t ecb_bitrev16 (uint16_t x) +{ + x = ((x >> 1) & 0x5555) | ((x & 0x5555) << 1); + x = ((x >> 2) & 0x3333) | ((x & 0x3333) << 2); + x = ((x >> 4) & 0x0f0f) | ((x & 0x0f0f) << 4); + x = ( x >> 8 ) | ( x << 8); + + return x; +} + +ecb_function_ ecb_const uint32_t ecb_bitrev32 (uint32_t x); +ecb_function_ ecb_const uint32_t ecb_bitrev32 (uint32_t x) +{ + x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1); + x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2); + x = ((x >> 4) & 0x0f0f0f0f) | ((x & 0x0f0f0f0f) << 4); + x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8); + x = ( x >> 16 ) | ( x << 16); + + return x; +} + +/* popcount64 is only available on 64 bit cpus as gcc builtin */ +/* so for this version we are lazy */ +ecb_function_ ecb_const int ecb_popcount64 (uint64_t x); +ecb_function_ ecb_const int +ecb_popcount64 (uint64_t x) +{ + return ecb_popcount32 (x) + ecb_popcount32 (x >> 32); +} + +ecb_inline ecb_const uint8_t ecb_rotl8 (uint8_t x, unsigned int count); +ecb_inline ecb_const uint8_t ecb_rotr8 (uint8_t x, unsigned int count); +ecb_inline ecb_const uint16_t ecb_rotl16 (uint16_t x, unsigned int count); +ecb_inline ecb_const uint16_t ecb_rotr16 (uint16_t x, unsigned int count); +ecb_inline ecb_const uint32_t ecb_rotl32 (uint32_t x, unsigned int count); +ecb_inline ecb_const uint32_t ecb_rotr32 (uint32_t x, unsigned int count); +ecb_inline ecb_const uint64_t ecb_rotl64 (uint64_t x, unsigned int count); +ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count); + +ecb_inline ecb_const uint8_t ecb_rotl8 (uint8_t x, unsigned int count) { return (x >> ( 8 - count)) | (x << count); } +ecb_inline ecb_const uint8_t ecb_rotr8 (uint8_t x, unsigned int count) { return (x << ( 8 - count)) | (x >> count); } +ecb_inline ecb_const uint16_t ecb_rotl16 (uint16_t x, unsigned int count) { return (x >> (16 - count)) | (x << count); } +ecb_inline ecb_const uint16_t ecb_rotr16 (uint16_t x, unsigned int count) { return (x << (16 - count)) | (x >> count); } +ecb_inline ecb_const uint32_t ecb_rotl32 (uint32_t x, unsigned int count) { return (x >> (32 - count)) | (x << count); } +ecb_inline ecb_const uint32_t ecb_rotr32 (uint32_t x, unsigned int count) { return (x << (32 - count)) | (x >> count); } +ecb_inline ecb_const uint64_t ecb_rotl64 (uint64_t x, unsigned int count) { return (x >> (64 - count)) | (x << count); } +ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { return (x << (64 - count)) | (x >> count); } + +#if ECB_GCC_VERSION(4,3) || (ECB_CLANG_BUILTIN(__builtin_bswap32) && ECB_CLANG_BUILTIN(__builtin_bswap64)) + #if ECB_GCC_VERSION(4,8) || ECB_CLANG_BUILTIN(__builtin_bswap16) + #define ecb_bswap16(x) __builtin_bswap16 (x) + #else + #define ecb_bswap16(x) (__builtin_bswap32 (x) >> 16) + #endif + #define ecb_bswap32(x) __builtin_bswap32 (x) + #define ecb_bswap64(x) __builtin_bswap64 (x) +#elif _MSC_VER + #include + #define ecb_bswap16(x) ((uint16_t)_byteswap_ushort ((uint16_t)(x))) + #define ecb_bswap32(x) ((uint32_t)_byteswap_ulong ((uint32_t)(x))) + #define ecb_bswap64(x) ((uint64_t)_byteswap_uint64 ((uint64_t)(x))) +#else + ecb_function_ ecb_const uint16_t ecb_bswap16 (uint16_t x); + ecb_function_ ecb_const uint16_t + ecb_bswap16 (uint16_t x) + { + return ecb_rotl16 (x, 8); + } + + ecb_function_ ecb_const uint32_t ecb_bswap32 (uint32_t x); + ecb_function_ ecb_const uint32_t + ecb_bswap32 (uint32_t x) + { + return (((uint32_t)ecb_bswap16 (x)) << 16) | ecb_bswap16 (x >> 16); + } + + ecb_function_ ecb_const uint64_t ecb_bswap64 (uint64_t x); + ecb_function_ ecb_const uint64_t + ecb_bswap64 (uint64_t x) + { + return (((uint64_t)ecb_bswap32 (x)) << 32) | ecb_bswap32 (x >> 32); + } +#endif + +#if ECB_GCC_VERSION(4,5) || ECB_CLANG_BUILTIN(__builtin_unreachable) + #define ecb_unreachable() __builtin_unreachable () +#else + /* this seems to work fine, but gcc always emits a warning for it :/ */ + ecb_inline ecb_noreturn void ecb_unreachable (void); + ecb_inline ecb_noreturn void ecb_unreachable (void) { } +#endif + +/* try to tell the compiler that some condition is definitely true */ +#define ecb_assume(cond) if (!(cond)) ecb_unreachable (); else 0 + +ecb_inline ecb_const unsigned char ecb_byteorder_helper (void); +ecb_inline ecb_const unsigned char +ecb_byteorder_helper (void) +{ + /* the union code still generates code under pressure in gcc, */ + /* but less than using pointers, and always seems to */ + /* successfully return a constant. */ + /* the reason why we have this horrible preprocessor mess */ + /* is to avoid it in all cases, at least on common architectures */ + /* or when using a recent enough gcc version (>= 4.6) */ +#if ((__i386 || __i386__) && !__VOS__) || _M_IX86 || ECB_GCC_AMD64 || ECB_MSVC_AMD64 + return 0x44; +#elif __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return 0x44; +#elif __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return 0x11; +#else + union + { + uint32_t i; + uint8_t c; + } u = { 0x11223344 }; + return u.c; +#endif +} + +ecb_inline ecb_const ecb_bool ecb_big_endian (void); +ecb_inline ecb_const ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11; } +ecb_inline ecb_const ecb_bool ecb_little_endian (void); +ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44; } + +#if ECB_GCC_VERSION(3,0) || ECB_C99 + #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0)) +#else + #define ecb_mod(m,n) ((m) < 0 ? ((n) - 1 - ((-1 - (m)) % (n))) : ((m) % (n))) +#endif + +#if ECB_CPP + template + static inline T ecb_div_rd (T val, T div) + { + return val < 0 ? - ((-val + div - 1) / div) : (val ) / div; + } + template + static inline T ecb_div_ru (T val, T div) + { + return val < 0 ? - ((-val ) / div) : (val + div - 1) / div; + } +#else + #define ecb_div_rd(val,div) ((val) < 0 ? - ((-(val) + (div) - 1) / (div)) : ((val) ) / (div)) + #define ecb_div_ru(val,div) ((val) < 0 ? - ((-(val) ) / (div)) : ((val) + (div) - 1) / (div)) +#endif + +#if ecb_cplusplus_does_not_suck + /* does not work for local types (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm) */ + template + static inline int ecb_array_length (const T (&arr)[N]) + { + return N; + } +#else + #define ecb_array_length(name) (sizeof (name) / sizeof (name [0])) +#endif + +/*******************************************************************************/ +/* floating point stuff, can be disabled by defining ECB_NO_LIBM */ + +/* basically, everything uses "ieee pure-endian" floating point numbers */ +/* the only noteworthy exception is ancient armle, which uses order 43218765 */ +#if 0 \ + || __i386 || __i386__ \ + || ECB_GCC_AMD64 \ + || __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__ \ + || defined __s390__ || defined __s390x__ \ + || defined __mips__ \ + || defined __alpha__ \ + || defined __hppa__ \ + || defined __ia64__ \ + || defined __m68k__ \ + || defined __m88k__ \ + || defined __sh__ \ + || defined _M_IX86 || defined ECB_MSVC_AMD64 || defined _M_IA64 \ + || (defined __arm__ && (defined __ARM_EABI__ || defined __EABI__ || defined __VFP_FP__ || defined _WIN32_WCE || defined __ANDROID__)) \ + || defined __aarch64__ + #define ECB_STDFP 1 + #include /* for memcpy */ +#else + #define ECB_STDFP 0 +#endif + +#ifndef ECB_NO_LIBM + + #include /* for frexp*, ldexp*, INFINITY, NAN */ + + /* only the oldest of old doesn't have this one. solaris. */ + #ifdef INFINITY + #define ECB_INFINITY INFINITY + #else + #define ECB_INFINITY HUGE_VAL + #endif + + #ifdef NAN + #define ECB_NAN NAN + #else + #define ECB_NAN ECB_INFINITY + #endif + + #if ECB_C99 || _XOPEN_VERSION >= 600 || _POSIX_VERSION >= 200112L + #define ecb_ldexpf(x,e) ldexpf ((x), (e)) + #define ecb_frexpf(x,e) frexpf ((x), (e)) + #else + #define ecb_ldexpf(x,e) (float) ldexp ((double) (x), (e)) + #define ecb_frexpf(x,e) (float) frexp ((double) (x), (e)) + #endif + + /* converts an ieee half/binary16 to a float */ + ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x); + ecb_function_ ecb_const float + ecb_binary16_to_float (uint16_t x) + { + int e = (x >> 10) & 0x1f; + int m = x & 0x3ff; + float r; + + if (!e ) r = ecb_ldexpf (m , -24); + else if (e != 31) r = ecb_ldexpf (m + 0x400, e - 25); + else if (m ) r = ECB_NAN; + else r = ECB_INFINITY; + + return x & 0x8000 ? -r : r; + } + + /* convert a float to ieee single/binary32 */ + ecb_function_ ecb_const uint32_t ecb_float_to_binary32 (float x); + ecb_function_ ecb_const uint32_t + ecb_float_to_binary32 (float x) + { + uint32_t r; + + #if ECB_STDFP + memcpy (&r, &x, 4); + #else + /* slow emulation, works for anything but -0 */ + uint32_t m; + int e; + + if (x == 0e0f ) return 0x00000000U; + if (x > +3.40282346638528860e+38f) return 0x7f800000U; + if (x < -3.40282346638528860e+38f) return 0xff800000U; + if (x != x ) return 0x7fbfffffU; + + m = ecb_frexpf (x, &e) * 0x1000000U; + + r = m & 0x80000000U; + + if (r) + m = -m; + + if (e <= -126) + { + m &= 0xffffffU; + m >>= (-125 - e); + e = -126; + } + + r |= (e + 126) << 23; + r |= m & 0x7fffffU; + #endif + + return r; + } + + /* converts an ieee single/binary32 to a float */ + ecb_function_ ecb_const float ecb_binary32_to_float (uint32_t x); + ecb_function_ ecb_const float + ecb_binary32_to_float (uint32_t x) + { + float r; + + #if ECB_STDFP + memcpy (&r, &x, 4); + #else + /* emulation, only works for normals and subnormals and +0 */ + int neg = x >> 31; + int e = (x >> 23) & 0xffU; + + x &= 0x7fffffU; + + if (e) + x |= 0x800000U; + else + e = 1; + + /* we distrust ldexpf a bit and do the 2**-24 scaling by an extra multiply */ + r = ecb_ldexpf (x * (0.5f / 0x800000U), e - 126); + + r = neg ? -r : r; + #endif + + return r; + } + + /* convert a double to ieee double/binary64 */ + ecb_function_ ecb_const uint64_t ecb_double_to_binary64 (double x); + ecb_function_ ecb_const uint64_t + ecb_double_to_binary64 (double x) + { + uint64_t r; + + #if ECB_STDFP + memcpy (&r, &x, 8); + #else + /* slow emulation, works for anything but -0 */ + uint64_t m; + int e; + + if (x == 0e0 ) return 0x0000000000000000U; + if (x > +1.79769313486231470e+308) return 0x7ff0000000000000U; + if (x < -1.79769313486231470e+308) return 0xfff0000000000000U; + if (x != x ) return 0X7ff7ffffffffffffU; + + m = frexp (x, &e) * 0x20000000000000U; + + r = m & 0x8000000000000000;; + + if (r) + m = -m; + + if (e <= -1022) + { + m &= 0x1fffffffffffffU; + m >>= (-1021 - e); + e = -1022; + } + + r |= ((uint64_t)(e + 1022)) << 52; + r |= m & 0xfffffffffffffU; + #endif + + return r; + } + + /* converts an ieee double/binary64 to a double */ + ecb_function_ ecb_const double ecb_binary64_to_double (uint64_t x); + ecb_function_ ecb_const double + ecb_binary64_to_double (uint64_t x) + { + double r; + + #if ECB_STDFP + memcpy (&r, &x, 8); + #else + /* emulation, only works for normals and subnormals and +0 */ + int neg = x >> 63; + int e = (x >> 52) & 0x7ffU; + + x &= 0xfffffffffffffU; + + if (e) + x |= 0x10000000000000U; + else + e = 1; + + /* we distrust ldexp a bit and do the 2**-53 scaling by an extra multiply */ + r = ldexp (x * (0.5 / 0x10000000000000U), e - 1022); + + r = neg ? -r : r; + #endif + + return r; + } + +#endif + +#endif + +/* ECB.H END */ + +#if ECB_MEMORY_FENCE_NEEDS_PTHREADS +/* if your architecture doesn't need memory fences, e.g. because it is + * single-cpu/core, or if you use libev in a project that doesn't use libev + * from multiple threads, then you can define ECB_AVOID_PTHREADS when compiling + * libev, in which cases the memory fences become nops. + * alternatively, you can remove this #error and link against libpthread, + * which will then provide the memory fences. + */ +# error "memory fences not defined for your architecture, please report" +#endif + +#ifndef ECB_MEMORY_FENCE +# define ECB_MEMORY_FENCE do { } while (0) +# define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE +# define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE +#endif + +#define expect_false(cond) ecb_expect_false (cond) +#define expect_true(cond) ecb_expect_true (cond) +#define noinline ecb_noinline + +#define inline_size ecb_inline + +#if EV_FEATURE_CODE +# define inline_speed ecb_inline +#else +# define inline_speed static noinline +#endif + +#define NUMPRI (EV_MAXPRI - EV_MINPRI + 1) + +#if EV_MINPRI == EV_MAXPRI +# define ABSPRI(w) (((W)w), 0) +#else +# define ABSPRI(w) (((W)w)->priority - EV_MINPRI) +#endif + +#define EMPTY /* required for microsofts broken pseudo-c compiler */ +#define EMPTY2(a,b) /* used to suppress some warnings */ + +typedef ev_watcher *W; +typedef ev_watcher_list *WL; +typedef ev_watcher_time *WT; + +#define ev_active(w) ((W)(w))->active +#define ev_at(w) ((WT)(w))->at + +#if EV_USE_REALTIME +/* sig_atomic_t is used to avoid per-thread variables or locking but still */ +/* giving it a reasonably high chance of working on typical architectures */ +static EV_ATOMIC_T have_realtime; /* did clock_gettime (CLOCK_REALTIME) work? */ +#endif + +#if EV_USE_MONOTONIC +static EV_ATOMIC_T have_monotonic; /* did clock_gettime (CLOCK_MONOTONIC) work? */ +#endif + +#ifndef EV_FD_TO_WIN32_HANDLE +# define EV_FD_TO_WIN32_HANDLE(fd) _get_osfhandle (fd) +#endif +#ifndef EV_WIN32_HANDLE_TO_FD +# define EV_WIN32_HANDLE_TO_FD(handle) _open_osfhandle (handle, 0) +#endif +#ifndef EV_WIN32_CLOSE_FD +# define EV_WIN32_CLOSE_FD(fd) close (fd) +#endif + +#ifdef _WIN32 +# include "ev_win32.c" +#endif + +/*****************************************************************************/ + +/* define a suitable floor function (only used by periodics atm) */ + +#if EV_USE_FLOOR +# include +# define ev_floor(v) floor (v) +#else + +#include + +/* a floor() replacement function, should be independent of ev_tstamp type */ +static ev_tstamp noinline +ev_floor (ev_tstamp v) +{ + /* the choice of shift factor is not terribly important */ +#if FLT_RADIX != 2 /* assume FLT_RADIX == 10 */ + const ev_tstamp shift = sizeof (unsigned long) >= 8 ? 10000000000000000000. : 1000000000.; +#else + const ev_tstamp shift = sizeof (unsigned long) >= 8 ? 18446744073709551616. : 4294967296.; +#endif + + /* argument too large for an unsigned long? */ + if (expect_false (v >= shift)) + { + ev_tstamp f; + + if (v == v - 1.) + return v; /* very large number */ + + f = shift * ev_floor (v * (1. / shift)); + return f + ev_floor (v - f); + } + + /* special treatment for negative args? */ + if (expect_false (v < 0.)) + { + ev_tstamp f = -ev_floor (-v); + + return f - (f == v ? 0 : 1); + } + + /* fits into an unsigned long */ + return (unsigned long)v; +} + +#endif + +/*****************************************************************************/ + +#ifdef __linux +# include +#endif + +static unsigned int noinline ecb_cold +ev_linux_version (void) +{ +#ifdef __linux + unsigned int v = 0; + struct utsname buf; + int i; + char *p = buf.release; + + if (uname (&buf)) + return 0; + + for (i = 3+1; --i; ) + { + unsigned int c = 0; + + for (;;) + { + if (*p >= '0' && *p <= '9') + c = c * 10 + *p++ - '0'; + else + { + p += *p == '.'; + break; + } + } + + v = (v << 8) | c; + } + + return v; +#else + return 0; +#endif +} + +/*****************************************************************************/ + +#if EV_AVOID_STDIO +static void noinline ecb_cold +ev_printerr (const char *msg) +{ + int rc; + do { + rc = write (STDERR_FILENO, msg, strlen (msg)); + } while (errno == EINTR && rc < 0); +} +#endif + +static void (*syserr_cb)(const char *msg) EV_THROW; + +void ecb_cold +ev_set_syserr_cb (void (*cb)(const char *msg) EV_THROW) EV_THROW +{ + syserr_cb = cb; +} + +static void noinline ecb_cold +ev_syserr (const char *msg) +{ + if (!msg) + msg = "(libev) system error"; + + if (syserr_cb) + syserr_cb (msg); + else + { +#if EV_AVOID_STDIO + ev_printerr (msg); + ev_printerr (": "); + ev_printerr (strerror (errno)); + ev_printerr ("\n"); +#else + perror (msg); +#endif + abort (); + } +} + +static void * +ev_realloc_emul (void *ptr, long size) EV_THROW +{ + /* some systems, notably openbsd and darwin, fail to properly + * implement realloc (x, 0) (as required by both ansi c-89 and + * the single unix specification, so work around them here. + * recently, also (at least) fedora and debian started breaking it, + * despite documenting it otherwise. + */ + + if (size) + return realloc (ptr, size); + + free (ptr); + return 0; +} + +static void *(*alloc)(void *ptr, long size) EV_THROW = ev_realloc_emul; + +void ecb_cold +ev_set_allocator (void *(*cb)(void *ptr, long size) EV_THROW) EV_THROW +{ + alloc = cb; +} + +inline_speed void * +ev_realloc (void *ptr, long size) +{ + ptr = alloc (ptr, size); + + if (!ptr && size) + { +#if EV_AVOID_STDIO + ev_printerr ("(libev) memory allocation failed, aborting.\n"); +#else + fprintf (stderr, "(libev) cannot allocate %ld bytes, aborting.", size); +#endif + abort (); + } + + return ptr; +} + +#define ev_malloc(size) ev_realloc (0, (size)) +#define ev_free(ptr) free (ptr) + +/*****************************************************************************/ + +/* set in reify when reification needed */ +#define EV_ANFD_REIFY 1 + +/* file descriptor info structure */ +typedef struct +{ + WL head; + unsigned char events; /* the events watched for */ + unsigned char reify; /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) */ + unsigned char emask; /* the epoll backend stores the actual kernel mask in here */ + unsigned char unused; +#if EV_USE_EPOLL + unsigned int egen; /* generation counter to counter epoll bugs */ +#endif +#if EV_SELECT_IS_WINSOCKET || EV_USE_IOCP + SOCKET handle; +#endif +#if EV_USE_IOCP + OVERLAPPED or, ow; +#endif +} ANFD; + +/* stores the pending event set for a given watcher */ +typedef struct +{ + W w; + int events; /* the pending event set for the given watcher */ +} ANPENDING; + +#if EV_USE_INOTIFY +/* hash table entry per inotify-id */ +typedef struct +{ + WL head; +} ANFS; +#endif + +/* Heap Entry */ +#if EV_HEAP_CACHE_AT + /* a heap element */ + typedef struct { + ev_tstamp at; + WT w; + } ANHE; + + #define ANHE_w(he) (he).w /* access watcher, read-write */ + #define ANHE_at(he) (he).at /* access cached at, read-only */ + #define ANHE_at_cache(he) (he).at = (he).w->at /* update at from watcher */ +#else + /* a heap element */ + typedef WT ANHE; + + #define ANHE_w(he) (he) + #define ANHE_at(he) (he)->at + #define ANHE_at_cache(he) +#endif + +#if EV_MULTIPLICITY + + struct ev_loop + { + ev_tstamp ev_rt_now; + #define ev_rt_now ((loop)->ev_rt_now) + #define VAR(name,decl) decl; + #include "ev_vars.h" + #undef VAR + }; + #include "ev_wrap.h" + + static struct ev_loop default_loop_struct; + EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */ + +#else + + EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */ + #define VAR(name,decl) static decl; + #include "ev_vars.h" + #undef VAR + + static int ev_default_loop_ptr; + +#endif + +#if EV_FEATURE_API +# define EV_RELEASE_CB if (expect_false (release_cb)) release_cb (EV_A) +# define EV_ACQUIRE_CB if (expect_false (acquire_cb)) acquire_cb (EV_A) +# define EV_INVOKE_PENDING invoke_cb (EV_A) +#else +# define EV_RELEASE_CB (void)0 +# define EV_ACQUIRE_CB (void)0 +# define EV_INVOKE_PENDING ev_invoke_pending (EV_A) +#endif + +#define EVBREAK_RECURSE 0x80 + +/*****************************************************************************/ + +#ifndef EV_HAVE_EV_TIME +ev_tstamp +ev_time (void) EV_THROW +{ +#if EV_USE_REALTIME + if (expect_true (have_realtime)) + { + struct timespec ts; + clock_gettime (CLOCK_REALTIME, &ts); + return ts.tv_sec + ts.tv_nsec * 1e-9; + } +#endif + + struct timeval tv; + gettimeofday (&tv, 0); + return tv.tv_sec + tv.tv_usec * 1e-6; +} +#endif + +inline_size ev_tstamp +get_clock (void) +{ +#if EV_USE_MONOTONIC + if (expect_true (have_monotonic)) + { + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec * 1e-9; + } +#endif + + return ev_time (); +} + +#if EV_MULTIPLICITY +ev_tstamp +ev_now (EV_P) EV_THROW +{ + return ev_rt_now; +} +#endif + +void +ev_sleep (ev_tstamp delay) EV_THROW +{ + if (delay > 0.) + { +#if EV_USE_NANOSLEEP + struct timespec ts; + + EV_TS_SET (ts, delay); + nanosleep (&ts, 0); +#elif defined _WIN32 + Sleep ((unsigned long)(delay * 1e3)); +#else + struct timeval tv; + + /* here we rely on sys/time.h + sys/types.h + unistd.h providing select */ + /* something not guaranteed by newer posix versions, but guaranteed */ + /* by older ones */ + EV_TV_SET (tv, delay); + select (0, 0, 0, 0, &tv); +#endif + } +} + +/*****************************************************************************/ + +#define MALLOC_ROUND 4096 /* prefer to allocate in chunks of this size, must be 2**n and >> 4 longs */ + +/* find a suitable new size for the given array, */ +/* hopefully by rounding to a nice-to-malloc size */ +inline_size int +array_nextsize (int elem, int cur, int cnt) +{ + int ncur = cur + 1; + + do + ncur <<= 1; + while (cnt > ncur); + + /* if size is large, round to MALLOC_ROUND - 4 * longs to accommodate malloc overhead */ + if (elem * ncur > MALLOC_ROUND - sizeof (void *) * 4) + { + ncur *= elem; + ncur = (ncur + elem + (MALLOC_ROUND - 1) + sizeof (void *) * 4) & ~(MALLOC_ROUND - 1); + ncur = ncur - sizeof (void *) * 4; + ncur /= elem; + } + + return ncur; +} + +static void * noinline ecb_cold +array_realloc (int elem, void *base, int *cur, int cnt) +{ + *cur = array_nextsize (elem, *cur, cnt); + return ev_realloc (base, elem * *cur); +} + +#define array_init_zero(base,count) \ + memset ((void *)(base), 0, sizeof (*(base)) * (count)) + +#define array_needsize(type,base,cur,cnt,init) \ + if (expect_false ((cnt) > (cur))) \ + { \ + int ecb_unused ocur_ = (cur); \ + (base) = (type *)array_realloc \ + (sizeof (type), (base), &(cur), (cnt)); \ + init ((base) + (ocur_), (cur) - ocur_); \ + } + +#if 0 +#define array_slim(type,stem) \ + if (stem ## max < array_roundsize (stem ## cnt >> 2)) \ + { \ + stem ## max = array_roundsize (stem ## cnt >> 1); \ + base = (type *)ev_realloc (base, sizeof (type) * (stem ## max));\ + fprintf (stderr, "slimmed down " # stem " to %d\n", stem ## max);/*D*/\ + } +#endif + +#define array_free(stem, idx) \ + ev_free (stem ## s idx); stem ## cnt idx = stem ## max idx = 0; stem ## s idx = 0 + +/*****************************************************************************/ + +/* dummy callback for pending events */ +static void noinline +pendingcb (EV_P_ ev_prepare *w, int revents) +{ +} + +void noinline +ev_feed_event (EV_P_ void *w, int revents) EV_THROW +{ + W w_ = (W)w; + int pri = ABSPRI (w_); + + if (expect_false (w_->pending)) + pendings [pri][w_->pending - 1].events |= revents; + else + { + w_->pending = ++pendingcnt [pri]; + array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_->pending, EMPTY2); + pendings [pri][w_->pending - 1].w = w_; + pendings [pri][w_->pending - 1].events = revents; + } + + pendingpri = NUMPRI - 1; +} + +inline_speed void +feed_reverse (EV_P_ W w) +{ + array_needsize (W, rfeeds, rfeedmax, rfeedcnt + 1, EMPTY2); + rfeeds [rfeedcnt++] = w; +} + +inline_size void +feed_reverse_done (EV_P_ int revents) +{ + do + ev_feed_event (EV_A_ rfeeds [--rfeedcnt], revents); + while (rfeedcnt); +} + +inline_speed void +queue_events (EV_P_ W *events, int eventcnt, int type) +{ + int i; + + for (i = 0; i < eventcnt; ++i) + ev_feed_event (EV_A_ events [i], type); +} + +/*****************************************************************************/ + +inline_speed void +fd_event_nocheck (EV_P_ int fd, int revents) +{ + ANFD *anfd = anfds + fd; + ev_io *w; + + for (w = (ev_io *)anfd->head; w; w = (ev_io *)((WL)w)->next) + { + int ev = w->events & revents; + + if (ev) + ev_feed_event (EV_A_ (W)w, ev); + } +} + +/* do not submit kernel events for fds that have reify set */ +/* because that means they changed while we were polling for new events */ +inline_speed void +fd_event (EV_P_ int fd, int revents) +{ + ANFD *anfd = anfds + fd; + + if (expect_true (!anfd->reify)) + fd_event_nocheck (EV_A_ fd, revents); +} + +void +ev_feed_fd_event (EV_P_ int fd, int revents) EV_THROW +{ + if (fd >= 0 && fd < anfdmax) + fd_event_nocheck (EV_A_ fd, revents); +} + +/* make sure the external fd watch events are in-sync */ +/* with the kernel/libev internal state */ +inline_size void +fd_reify (EV_P) +{ + int i; + +#if EV_SELECT_IS_WINSOCKET || EV_USE_IOCP + for (i = 0; i < fdchangecnt; ++i) + { + int fd = fdchanges [i]; + ANFD *anfd = anfds + fd; + + if (anfd->reify & EV__IOFDSET && anfd->head) + { + SOCKET handle = EV_FD_TO_WIN32_HANDLE (fd); + + if (handle != anfd->handle) + { + unsigned long arg; + + assert (("libev: only socket fds supported in this configuration", ioctlsocket (handle, FIONREAD, &arg) == 0)); + + /* handle changed, but fd didn't - we need to do it in two steps */ + backend_modify (EV_A_ fd, anfd->events, 0); + anfd->events = 0; + anfd->handle = handle; + } + } + } +#endif + + for (i = 0; i < fdchangecnt; ++i) + { + int fd = fdchanges [i]; + ANFD *anfd = anfds + fd; + ev_io *w; + + unsigned char o_events = anfd->events; + unsigned char o_reify = anfd->reify; + + anfd->reify = 0; + + /*if (expect_true (o_reify & EV_ANFD_REIFY)) probably a deoptimisation */ + { + anfd->events = 0; + + for (w = (ev_io *)anfd->head; w; w = (ev_io *)((WL)w)->next) + anfd->events |= (unsigned char)w->events; + + if (o_events != anfd->events) + o_reify = EV__IOFDSET; /* actually |= */ + } + + if (o_reify & EV__IOFDSET) + backend_modify (EV_A_ fd, o_events, anfd->events); + } + + fdchangecnt = 0; +} + +/* something about the given fd changed */ +inline_size void +fd_change (EV_P_ int fd, int flags) +{ + unsigned char reify = anfds [fd].reify; + anfds [fd].reify |= flags; + + if (expect_true (!reify)) + { + ++fdchangecnt; + array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2); + fdchanges [fdchangecnt - 1] = fd; + } +} + +/* the given fd is invalid/unusable, so make sure it doesn't hurt us anymore */ +inline_speed void ecb_cold +fd_kill (EV_P_ int fd) +{ + ev_io *w; + + while ((w = (ev_io *)anfds [fd].head)) + { + ev_io_stop (EV_A_ w); + ev_feed_event (EV_A_ (W)w, EV_ERROR | EV_READ | EV_WRITE); + } +} + +/* check whether the given fd is actually valid, for error recovery */ +inline_size int ecb_cold +fd_valid (int fd) +{ +#ifdef _WIN32 + return EV_FD_TO_WIN32_HANDLE (fd) != -1; +#else + return fcntl (fd, F_GETFD) != -1; +#endif +} + +/* called on EBADF to verify fds */ +static void noinline ecb_cold +fd_ebadf (EV_P) +{ + int fd; + + for (fd = 0; fd < anfdmax; ++fd) + if (anfds [fd].events) + if (!fd_valid (fd) && errno == EBADF) + fd_kill (EV_A_ fd); +} + +/* called on ENOMEM in select/poll to kill some fds and retry */ +static void noinline ecb_cold +fd_enomem (EV_P) +{ + int fd; + + for (fd = anfdmax; fd--; ) + if (anfds [fd].events) + { + fd_kill (EV_A_ fd); + break; + } +} + +/* usually called after fork if backend needs to re-arm all fds from scratch */ +static void noinline +fd_rearm_all (EV_P) +{ + int fd; + + for (fd = 0; fd < anfdmax; ++fd) + if (anfds [fd].events) + { + anfds [fd].events = 0; + anfds [fd].emask = 0; + fd_change (EV_A_ fd, EV__IOFDSET | EV_ANFD_REIFY); + } +} + +/* used to prepare libev internal fd's */ +/* this is not fork-safe */ +inline_speed void +fd_intern (int fd) +{ +#ifdef _WIN32 + unsigned long arg = 1; + ioctlsocket (EV_FD_TO_WIN32_HANDLE (fd), FIONBIO, &arg); +#else + fcntl (fd, F_SETFD, FD_CLOEXEC); + fcntl (fd, F_SETFL, O_NONBLOCK); +#endif +} + +/*****************************************************************************/ + +/* + * the heap functions want a real array index. array index 0 is guaranteed to not + * be in-use at any time. the first heap entry is at array [HEAP0]. DHEAP gives + * the branching factor of the d-tree. + */ + +/* + * at the moment we allow libev the luxury of two heaps, + * a small-code-size 2-heap one and a ~1.5kb larger 4-heap + * which is more cache-efficient. + * the difference is about 5% with 50000+ watchers. + */ +#if EV_USE_4HEAP + +#define DHEAP 4 +#define HEAP0 (DHEAP - 1) /* index of first element in heap */ +#define HPARENT(k) ((((k) - HEAP0 - 1) / DHEAP) + HEAP0) +#define UPHEAP_DONE(p,k) ((p) == (k)) + +/* away from the root */ +inline_speed void +downheap (ANHE *heap, int N, int k) +{ + ANHE he = heap [k]; + ANHE *E = heap + N + HEAP0; + + for (;;) + { + ev_tstamp minat; + ANHE *minpos; + ANHE *pos = heap + DHEAP * (k - HEAP0) + HEAP0 + 1; + + /* find minimum child */ + if (expect_true (pos + DHEAP - 1 < E)) + { + /* fast path */ (minpos = pos + 0), (minat = ANHE_at (*minpos)); + if ( ANHE_at (pos [1]) < minat) (minpos = pos + 1), (minat = ANHE_at (*minpos)); + if ( ANHE_at (pos [2]) < minat) (minpos = pos + 2), (minat = ANHE_at (*minpos)); + if ( ANHE_at (pos [3]) < minat) (minpos = pos + 3), (minat = ANHE_at (*minpos)); + } + else if (pos < E) + { + /* slow path */ (minpos = pos + 0), (minat = ANHE_at (*minpos)); + if (pos + 1 < E && ANHE_at (pos [1]) < minat) (minpos = pos + 1), (minat = ANHE_at (*minpos)); + if (pos + 2 < E && ANHE_at (pos [2]) < minat) (minpos = pos + 2), (minat = ANHE_at (*minpos)); + if (pos + 3 < E && ANHE_at (pos [3]) < minat) (minpos = pos + 3), (minat = ANHE_at (*minpos)); + } + else + break; + + if (ANHE_at (he) <= minat) + break; + + heap [k] = *minpos; + ev_active (ANHE_w (*minpos)) = k; + + k = minpos - heap; + } + + heap [k] = he; + ev_active (ANHE_w (he)) = k; +} + +#else /* 4HEAP */ + +#define HEAP0 1 +#define HPARENT(k) ((k) >> 1) +#define UPHEAP_DONE(p,k) (!(p)) + +/* away from the root */ +inline_speed void +downheap (ANHE *heap, int N, int k) +{ + ANHE he = heap [k]; + + for (;;) + { + int c = k << 1; + + if (c >= N + HEAP0) + break; + + c += c + 1 < N + HEAP0 && ANHE_at (heap [c]) > ANHE_at (heap [c + 1]) + ? 1 : 0; + + if (ANHE_at (he) <= ANHE_at (heap [c])) + break; + + heap [k] = heap [c]; + ev_active (ANHE_w (heap [k])) = k; + + k = c; + } + + heap [k] = he; + ev_active (ANHE_w (he)) = k; +} +#endif + +/* towards the root */ +inline_speed void +upheap (ANHE *heap, int k) +{ + ANHE he = heap [k]; + + for (;;) + { + int p = HPARENT (k); + + if (UPHEAP_DONE (p, k) || ANHE_at (heap [p]) <= ANHE_at (he)) + break; + + heap [k] = heap [p]; + ev_active (ANHE_w (heap [k])) = k; + k = p; + } + + heap [k] = he; + ev_active (ANHE_w (he)) = k; +} + +/* move an element suitably so it is in a correct place */ +inline_size void +adjustheap (ANHE *heap, int N, int k) +{ + if (k > HEAP0 && ANHE_at (heap [k]) <= ANHE_at (heap [HPARENT (k)])) + upheap (heap, k); + else + downheap (heap, N, k); +} + +/* rebuild the heap: this function is used only once and executed rarely */ +inline_size void +reheap (ANHE *heap, int N) +{ + int i; + + /* we don't use floyds algorithm, upheap is simpler and is more cache-efficient */ + /* also, this is easy to implement and correct for both 2-heaps and 4-heaps */ + for (i = 0; i < N; ++i) + upheap (heap, i + HEAP0); +} + +/*****************************************************************************/ + +/* associate signal watchers to a signal signal */ +typedef struct +{ + EV_ATOMIC_T pending; +#if EV_MULTIPLICITY + EV_P; +#endif + WL head; +} ANSIG; + +static ANSIG signals [EV_NSIG]; + +/*****************************************************************************/ + +#if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE + +static void noinline ecb_cold +evpipe_init (EV_P) +{ + if (!ev_is_active (&pipe_w)) + { + int fds [2]; + +# if EV_USE_EVENTFD + fds [0] = -1; + fds [1] = eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC); + if (fds [1] < 0 && errno == EINVAL) + fds [1] = eventfd (0, 0); + + if (fds [1] < 0) +# endif + { + while (pipe (fds)) + ev_syserr ("(libev) error creating signal/async pipe"); + + fd_intern (fds [0]); + } + + evpipe [0] = fds [0]; + + if (evpipe [1] < 0) + evpipe [1] = fds [1]; /* first call, set write fd */ + else + { + /* on subsequent calls, do not change evpipe [1] */ + /* so that evpipe_write can always rely on its value. */ + /* this branch does not do anything sensible on windows, */ + /* so must not be executed on windows */ + + dup2 (fds [1], evpipe [1]); + close (fds [1]); + } + + fd_intern (evpipe [1]); + + ev_io_set (&pipe_w, evpipe [0] < 0 ? evpipe [1] : evpipe [0], EV_READ); + ev_io_start (EV_A_ &pipe_w); + ev_unref (EV_A); /* watcher should not keep loop alive */ + } +} + +inline_speed void +evpipe_write (EV_P_ EV_ATOMIC_T *flag) +{ + ECB_MEMORY_FENCE; /* push out the write before this function was called, acquire flag */ + + if (expect_true (*flag)) + return; + + *flag = 1; + ECB_MEMORY_FENCE_RELEASE; /* make sure flag is visible before the wakeup */ + + pipe_write_skipped = 1; + + ECB_MEMORY_FENCE; /* make sure pipe_write_skipped is visible before we check pipe_write_wanted */ + + if (pipe_write_wanted) + { + int old_errno, rc; + + pipe_write_skipped = 0; + ECB_MEMORY_FENCE_RELEASE; + + old_errno = errno; /* save errno because write will clobber it */ + +#if EV_USE_EVENTFD + if (evpipe [0] < 0) + { + uint64_t counter = 1; + do { + rc = write (evpipe [1], &counter, sizeof (uint64_t)); + } while (errno == EINTR && rc < 0); + } + else +#endif + { +#ifdef _WIN32 + WSABUF buf; + DWORD sent; + buf.buf = &buf; + buf.len = 1; + WSASend (EV_FD_TO_WIN32_HANDLE (evpipe [1]), &buf, 1, &sent, 0, 0, 0); +#else + do { + rc = write (evpipe [1], &(evpipe [1]), 1); + } while (errno == EINTR && rc < 0); +#endif + } + + errno = old_errno; + } +} + +/* called whenever the libev signal pipe */ +/* got some events (signal, async) */ +static void +pipecb (EV_P_ ev_io *iow, int revents) +{ + int i; + + if (revents & EV_READ) + { +#if EV_USE_EVENTFD + if (evpipe [0] < 0) + { + uint64_t counter; + read (evpipe [1], &counter, sizeof (uint64_t)); + } + else +#endif + { + char dummy[4]; +#ifdef _WIN32 + WSABUF buf; + DWORD recvd; + DWORD flags = 0; + buf.buf = dummy; + buf.len = sizeof (dummy); + WSARecv (EV_FD_TO_WIN32_HANDLE (evpipe [0]), &buf, 1, &recvd, &flags, 0, 0); +#else + read (evpipe [0], &dummy, sizeof (dummy)); +#endif + } + } + + pipe_write_skipped = 0; + + ECB_MEMORY_FENCE; /* push out skipped, acquire flags */ + +#if EV_SIGNAL_ENABLE + if (sig_pending) + { + sig_pending = 0; + + ECB_MEMORY_FENCE; + + for (i = EV_NSIG - 1; i--; ) + if (expect_false (signals [i].pending)) + ev_feed_signal_event (EV_A_ i + 1); + } +#endif + +#if EV_ASYNC_ENABLE + if (async_pending) + { + async_pending = 0; + + ECB_MEMORY_FENCE; + + for (i = asynccnt; i--; ) + if (asyncs [i]->sent) + { + asyncs [i]->sent = 0; + ECB_MEMORY_FENCE_RELEASE; + ev_feed_event (EV_A_ asyncs [i], EV_ASYNC); + } + } +#endif +} + +/*****************************************************************************/ + +void +ev_feed_signal (int signum) EV_THROW +{ +#if EV_MULTIPLICITY + EV_P; + ECB_MEMORY_FENCE_ACQUIRE; + EV_A = signals [signum - 1].loop; + + if (!EV_A) + return; +#endif + + signals [signum - 1].pending = 1; + evpipe_write (EV_A_ &sig_pending); +} + +static void +ev_sighandler (int signum) +{ +#ifdef _WIN32 + signal (signum, ev_sighandler); +#endif + + ev_feed_signal (signum); +} + +void noinline +ev_feed_signal_event (EV_P_ int signum) EV_THROW +{ + WL w; + + if (expect_false (signum <= 0 || signum >= EV_NSIG)) + return; + + --signum; + +#if EV_MULTIPLICITY + /* it is permissible to try to feed a signal to the wrong loop */ + /* or, likely more useful, feeding a signal nobody is waiting for */ + + if (expect_false (signals [signum].loop != EV_A)) + return; +#endif + + signals [signum].pending = 0; + ECB_MEMORY_FENCE_RELEASE; + + for (w = signals [signum].head; w; w = w->next) + ev_feed_event (EV_A_ (W)w, EV_SIGNAL); +} + +#if EV_USE_SIGNALFD +static void +sigfdcb (EV_P_ ev_io *iow, int revents) +{ + struct signalfd_siginfo si[2], *sip; /* these structs are big */ + + for (;;) + { + ssize_t res = read (sigfd, si, sizeof (si)); + + /* not ISO-C, as res might be -1, but works with SuS */ + for (sip = si; (char *)sip < (char *)si + res; ++sip) + ev_feed_signal_event (EV_A_ sip->ssi_signo); + + if (res < (ssize_t)sizeof (si)) + break; + } +} +#endif + +#endif + +/*****************************************************************************/ + +#if EV_CHILD_ENABLE +static WL childs [EV_PID_HASHSIZE]; + +static ev_signal childev; + +#ifndef WIFCONTINUED +# define WIFCONTINUED(status) 0 +#endif + +/* handle a single child status event */ +inline_speed void +child_reap (EV_P_ int chain, int pid, int status) +{ + ev_child *w; + int traced = WIFSTOPPED (status) || WIFCONTINUED (status); + + for (w = (ev_child *)childs [chain & ((EV_PID_HASHSIZE) - 1)]; w; w = (ev_child *)((WL)w)->next) + { + if ((w->pid == pid || !w->pid) + && (!traced || (w->flags & 1))) + { + ev_set_priority (w, EV_MAXPRI); /* need to do it *now*, this *must* be the same prio as the signal watcher itself */ + w->rpid = pid; + w->rstatus = status; + ev_feed_event (EV_A_ (W)w, EV_CHILD); + } + } +} + +#ifndef WCONTINUED +# define WCONTINUED 0 +#endif + +/* called on sigchld etc., calls waitpid */ +static void +childcb (EV_P_ ev_signal *sw, int revents) +{ + int pid, status; + + /* some systems define WCONTINUED but then fail to support it (linux 2.4) */ + if (0 >= (pid = waitpid (-1, &status, WNOHANG | WUNTRACED | WCONTINUED))) + if (!WCONTINUED + || errno != EINVAL + || 0 >= (pid = waitpid (-1, &status, WNOHANG | WUNTRACED))) + return; + + /* make sure we are called again until all children have been reaped */ + /* we need to do it this way so that the callback gets called before we continue */ + ev_feed_event (EV_A_ (W)sw, EV_SIGNAL); + + child_reap (EV_A_ pid, pid, status); + if ((EV_PID_HASHSIZE) > 1) + child_reap (EV_A_ 0, pid, status); /* this might trigger a watcher twice, but feed_event catches that */ +} + +#endif + +/*****************************************************************************/ + +#if EV_USE_IOCP +# include "ev_iocp.c" +#endif +#if EV_USE_PORT +# include "ev_port.c" +#endif +#if EV_USE_KQUEUE +# include "ev_kqueue.c" +#endif +#if EV_USE_EPOLL +# include "ev_epoll.c" +#endif +#if EV_USE_POLL +# include "ev_poll.c" +#endif +#if EV_USE_SELECT +# include "ev_select.c" +#endif + +int ecb_cold +ev_version_major (void) EV_THROW +{ + return EV_VERSION_MAJOR; +} + +int ecb_cold +ev_version_minor (void) EV_THROW +{ + return EV_VERSION_MINOR; +} + +/* return true if we are running with elevated privileges and should ignore env variables */ +int inline_size ecb_cold +enable_secure (void) +{ +#ifdef _WIN32 + return 0; +#else + return getuid () != geteuid () + || getgid () != getegid (); +#endif +} + +unsigned int ecb_cold +ev_supported_backends (void) EV_THROW +{ + unsigned int flags = 0; + + if (EV_USE_PORT ) flags |= EVBACKEND_PORT; + if (EV_USE_KQUEUE) flags |= EVBACKEND_KQUEUE; + if (EV_USE_EPOLL ) flags |= EVBACKEND_EPOLL; + if (EV_USE_POLL ) flags |= EVBACKEND_POLL; + if (EV_USE_SELECT) flags |= EVBACKEND_SELECT; + + return flags; +} + +unsigned int ecb_cold +ev_recommended_backends (void) EV_THROW +{ + unsigned int flags = ev_supported_backends (); + +#ifndef __NetBSD__ + /* kqueue is borked on everything but netbsd apparently */ + /* it usually doesn't work correctly on anything but sockets and pipes */ + flags &= ~EVBACKEND_KQUEUE; +#endif +#ifdef __APPLE__ + /* only select works correctly on that "unix-certified" platform */ + flags &= ~EVBACKEND_KQUEUE; /* horribly broken, even for sockets */ + flags &= ~EVBACKEND_POLL; /* poll is based on kqueue from 10.5 onwards */ +#endif +#ifdef __FreeBSD__ + flags &= ~EVBACKEND_POLL; /* poll return value is unusable (http://forums.freebsd.org/archive/index.php/t-10270.html) */ +#endif + + return flags; +} + +unsigned int ecb_cold +ev_embeddable_backends (void) EV_THROW +{ + int flags = EVBACKEND_EPOLL | EVBACKEND_KQUEUE | EVBACKEND_PORT; + + /* epoll embeddability broken on all linux versions up to at least 2.6.23 */ + if (ev_linux_version () < 0x020620) /* disable it on linux < 2.6.32 */ + flags &= ~EVBACKEND_EPOLL; + + return flags; +} + +unsigned int +ev_backend (EV_P) EV_THROW +{ + return backend; +} + +#if EV_FEATURE_API +unsigned int +ev_iteration (EV_P) EV_THROW +{ + return loop_count; +} + +unsigned int +ev_depth (EV_P) EV_THROW +{ + return loop_depth; +} + +void +ev_set_io_collect_interval (EV_P_ ev_tstamp interval) EV_THROW +{ + io_blocktime = interval; +} + +void +ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval) EV_THROW +{ + timeout_blocktime = interval; +} + +void +ev_set_userdata (EV_P_ void *data) EV_THROW +{ + userdata = data; +} + +void * +ev_userdata (EV_P) EV_THROW +{ + return userdata; +} + +void +ev_set_invoke_pending_cb (EV_P_ ev_loop_callback invoke_pending_cb) EV_THROW +{ + invoke_cb = invoke_pending_cb; +} + +void +ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_THROW, void (*acquire)(EV_P) EV_THROW) EV_THROW +{ + release_cb = release; + acquire_cb = acquire; +} +#endif + +/* initialise a loop structure, must be zero-initialised */ +static void noinline ecb_cold +loop_init (EV_P_ unsigned int flags) EV_THROW +{ + if (!backend) + { + origflags = flags; + +#if EV_USE_REALTIME + if (!have_realtime) + { + struct timespec ts; + + if (!clock_gettime (CLOCK_REALTIME, &ts)) + have_realtime = 1; + } +#endif + +#if EV_USE_MONOTONIC + if (!have_monotonic) + { + struct timespec ts; + + if (!clock_gettime (CLOCK_MONOTONIC, &ts)) + have_monotonic = 1; + } +#endif + + /* pid check not overridable via env */ +#ifndef _WIN32 + if (flags & EVFLAG_FORKCHECK) + curpid = getpid (); +#endif + + if (!(flags & EVFLAG_NOENV) + && !enable_secure () + && getenv ("LIBEV_FLAGS")) + flags = atoi (getenv ("LIBEV_FLAGS")); + + ev_rt_now = ev_time (); + mn_now = get_clock (); + now_floor = mn_now; + rtmn_diff = ev_rt_now - mn_now; +#if EV_FEATURE_API + invoke_cb = ev_invoke_pending; +#endif + + io_blocktime = 0.; + timeout_blocktime = 0.; + backend = 0; + backend_fd = -1; + sig_pending = 0; +#if EV_ASYNC_ENABLE + async_pending = 0; +#endif + pipe_write_skipped = 0; + pipe_write_wanted = 0; + evpipe [0] = -1; + evpipe [1] = -1; +#if EV_USE_INOTIFY + fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2; +#endif +#if EV_USE_SIGNALFD + sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1; +#endif + + if (!(flags & EVBACKEND_MASK)) + flags |= ev_recommended_backends (); + +#if EV_USE_IOCP + if (!backend && (flags & EVBACKEND_IOCP )) backend = iocp_init (EV_A_ flags); +#endif +#if EV_USE_PORT + if (!backend && (flags & EVBACKEND_PORT )) backend = port_init (EV_A_ flags); +#endif +#if EV_USE_KQUEUE + if (!backend && (flags & EVBACKEND_KQUEUE)) backend = kqueue_init (EV_A_ flags); +#endif +#if EV_USE_EPOLL + if (!backend && (flags & EVBACKEND_EPOLL )) backend = epoll_init (EV_A_ flags); +#endif +#if EV_USE_POLL + if (!backend && (flags & EVBACKEND_POLL )) backend = poll_init (EV_A_ flags); +#endif +#if EV_USE_SELECT + if (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags); +#endif + + ev_prepare_init (&pending_w, pendingcb); + +#if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE + ev_init (&pipe_w, pipecb); + ev_set_priority (&pipe_w, EV_MAXPRI); +#endif + } +} + +/* free up a loop structure */ +void ecb_cold +ev_loop_destroy (EV_P) +{ + int i; + +#if EV_MULTIPLICITY + /* mimic free (0) */ + if (!EV_A) + return; +#endif + +#if EV_CLEANUP_ENABLE + /* queue cleanup watchers (and execute them) */ + if (expect_false (cleanupcnt)) + { + queue_events (EV_A_ (W *)cleanups, cleanupcnt, EV_CLEANUP); + EV_INVOKE_PENDING; + } +#endif + +#if EV_CHILD_ENABLE + if (ev_is_default_loop (EV_A) && ev_is_active (&childev)) + { + ev_ref (EV_A); /* child watcher */ + ev_signal_stop (EV_A_ &childev); + } +#endif + + if (ev_is_active (&pipe_w)) + { + /*ev_ref (EV_A);*/ + /*ev_io_stop (EV_A_ &pipe_w);*/ + + if (evpipe [0] >= 0) EV_WIN32_CLOSE_FD (evpipe [0]); + if (evpipe [1] >= 0) EV_WIN32_CLOSE_FD (evpipe [1]); + } + +#if EV_USE_SIGNALFD + if (ev_is_active (&sigfd_w)) + close (sigfd); +#endif + +#if EV_USE_INOTIFY + if (fs_fd >= 0) + close (fs_fd); +#endif + + if (backend_fd >= 0) + close (backend_fd); + +#if EV_USE_IOCP + if (backend == EVBACKEND_IOCP ) iocp_destroy (EV_A); +#endif +#if EV_USE_PORT + if (backend == EVBACKEND_PORT ) port_destroy (EV_A); +#endif +#if EV_USE_KQUEUE + if (backend == EVBACKEND_KQUEUE) kqueue_destroy (EV_A); +#endif +#if EV_USE_EPOLL + if (backend == EVBACKEND_EPOLL ) epoll_destroy (EV_A); +#endif +#if EV_USE_POLL + if (backend == EVBACKEND_POLL ) poll_destroy (EV_A); +#endif +#if EV_USE_SELECT + if (backend == EVBACKEND_SELECT) select_destroy (EV_A); +#endif + + for (i = NUMPRI; i--; ) + { + array_free (pending, [i]); +#if EV_IDLE_ENABLE + array_free (idle, [i]); +#endif + } + + ev_free (anfds); anfds = 0; anfdmax = 0; + + /* have to use the microsoft-never-gets-it-right macro */ + array_free (rfeed, EMPTY); + array_free (fdchange, EMPTY); + array_free (timer, EMPTY); +#if EV_PERIODIC_ENABLE + array_free (periodic, EMPTY); +#endif +#if EV_FORK_ENABLE + array_free (fork, EMPTY); +#endif +#if EV_CLEANUP_ENABLE + array_free (cleanup, EMPTY); +#endif + array_free (prepare, EMPTY); + array_free (check, EMPTY); +#if EV_ASYNC_ENABLE + array_free (async, EMPTY); +#endif + + backend = 0; + +#if EV_MULTIPLICITY + if (ev_is_default_loop (EV_A)) +#endif + ev_default_loop_ptr = 0; +#if EV_MULTIPLICITY + else + ev_free (EV_A); +#endif +} + +#if EV_USE_INOTIFY +inline_size void infy_fork (EV_P); +#endif + +inline_size void +loop_fork (EV_P) +{ +#if EV_USE_PORT + if (backend == EVBACKEND_PORT ) port_fork (EV_A); +#endif +#if EV_USE_KQUEUE + if (backend == EVBACKEND_KQUEUE) kqueue_fork (EV_A); +#endif +#if EV_USE_EPOLL + if (backend == EVBACKEND_EPOLL ) epoll_fork (EV_A); +#endif +#if EV_USE_INOTIFY + infy_fork (EV_A); +#endif + +#if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE + if (ev_is_active (&pipe_w)) + { + /* pipe_write_wanted must be false now, so modifying fd vars should be safe */ + + ev_ref (EV_A); + ev_io_stop (EV_A_ &pipe_w); + + if (evpipe [0] >= 0) + EV_WIN32_CLOSE_FD (evpipe [0]); + + evpipe_init (EV_A); + /* iterate over everything, in case we missed something before */ + ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM); + } +#endif + + postfork = 0; +} + +#if EV_MULTIPLICITY + +struct ev_loop * ecb_cold +ev_loop_new (unsigned int flags) EV_THROW +{ + EV_P = (struct ev_loop *)ev_malloc (sizeof (struct ev_loop)); + + memset (EV_A, 0, sizeof (struct ev_loop)); + loop_init (EV_A_ flags); + + if (ev_backend (EV_A)) + return EV_A; + + ev_free (EV_A); + return 0; +} + +#endif /* multiplicity */ + +#if EV_VERIFY +static void noinline ecb_cold +verify_watcher (EV_P_ W w) +{ + assert (("libev: watcher has invalid priority", ABSPRI (w) >= 0 && ABSPRI (w) < NUMPRI)); + + if (w->pending) + assert (("libev: pending watcher not on pending queue", pendings [ABSPRI (w)][w->pending - 1].w == w)); +} + +static void noinline ecb_cold +verify_heap (EV_P_ ANHE *heap, int N) +{ + int i; + + for (i = HEAP0; i < N + HEAP0; ++i) + { + assert (("libev: active index mismatch in heap", ev_active (ANHE_w (heap [i])) == i)); + assert (("libev: heap condition violated", i == HEAP0 || ANHE_at (heap [HPARENT (i)]) <= ANHE_at (heap [i]))); + assert (("libev: heap at cache mismatch", ANHE_at (heap [i]) == ev_at (ANHE_w (heap [i])))); + + verify_watcher (EV_A_ (W)ANHE_w (heap [i])); + } +} + +static void noinline ecb_cold +array_verify (EV_P_ W *ws, int cnt) +{ + while (cnt--) + { + assert (("libev: active index mismatch", ev_active (ws [cnt]) == cnt + 1)); + verify_watcher (EV_A_ ws [cnt]); + } +} +#endif + +#if EV_FEATURE_API +void ecb_cold +ev_verify (EV_P) EV_THROW +{ +#if EV_VERIFY + int i; + WL w, w2; + + assert (activecnt >= -1); + + assert (fdchangemax >= fdchangecnt); + for (i = 0; i < fdchangecnt; ++i) + assert (("libev: negative fd in fdchanges", fdchanges [i] >= 0)); + + assert (anfdmax >= 0); + for (i = 0; i < anfdmax; ++i) + { + int j = 0; + + for (w = w2 = anfds [i].head; w; w = w->next) + { + verify_watcher (EV_A_ (W)w); + + if (j++ & 1) + { + assert (("libev: io watcher list contains a loop", w != w2)); + w2 = w2->next; + } + + assert (("libev: inactive fd watcher on anfd list", ev_active (w) == 1)); + assert (("libev: fd mismatch between watcher and anfd", ((ev_io *)w)->fd == i)); + } + } + + assert (timermax >= timercnt); + verify_heap (EV_A_ timers, timercnt); + +#if EV_PERIODIC_ENABLE + assert (periodicmax >= periodiccnt); + verify_heap (EV_A_ periodics, periodiccnt); +#endif + + for (i = NUMPRI; i--; ) + { + assert (pendingmax [i] >= pendingcnt [i]); +#if EV_IDLE_ENABLE + assert (idleall >= 0); + assert (idlemax [i] >= idlecnt [i]); + array_verify (EV_A_ (W *)idles [i], idlecnt [i]); +#endif + } + +#if EV_FORK_ENABLE + assert (forkmax >= forkcnt); + array_verify (EV_A_ (W *)forks, forkcnt); +#endif + +#if EV_CLEANUP_ENABLE + assert (cleanupmax >= cleanupcnt); + array_verify (EV_A_ (W *)cleanups, cleanupcnt); +#endif + +#if EV_ASYNC_ENABLE + assert (asyncmax >= asynccnt); + array_verify (EV_A_ (W *)asyncs, asynccnt); +#endif + +#if EV_PREPARE_ENABLE + assert (preparemax >= preparecnt); + array_verify (EV_A_ (W *)prepares, preparecnt); +#endif + +#if EV_CHECK_ENABLE + assert (checkmax >= checkcnt); + array_verify (EV_A_ (W *)checks, checkcnt); +#endif + +# if 0 +#if EV_CHILD_ENABLE + for (w = (ev_child *)childs [chain & ((EV_PID_HASHSIZE) - 1)]; w; w = (ev_child *)((WL)w)->next) + for (signum = EV_NSIG - 1; signum--; ) if (signals [signum].pending) +#endif +# endif +#endif +} +#endif + +#if EV_MULTIPLICITY +struct ev_loop * ecb_cold +#else +int +#endif +ev_default_loop (unsigned int flags) EV_THROW +{ + if (!ev_default_loop_ptr) + { +#if EV_MULTIPLICITY + EV_P = ev_default_loop_ptr = &default_loop_struct; +#else + ev_default_loop_ptr = 1; +#endif + + loop_init (EV_A_ flags); + + if (ev_backend (EV_A)) + { +#if EV_CHILD_ENABLE + ev_signal_init (&childev, childcb, SIGCHLD); + ev_set_priority (&childev, EV_MAXPRI); + ev_signal_start (EV_A_ &childev); + ev_unref (EV_A); /* child watcher should not keep loop alive */ +#endif + } + else + ev_default_loop_ptr = 0; + } + + return ev_default_loop_ptr; +} + +void +ev_loop_fork (EV_P) EV_THROW +{ + postfork = 1; +} + +/*****************************************************************************/ + +void +ev_invoke (EV_P_ void *w, int revents) +{ + EV_CB_INVOKE ((W)w, revents); +} + +unsigned int +ev_pending_count (EV_P) EV_THROW +{ + int pri; + unsigned int count = 0; + + for (pri = NUMPRI; pri--; ) + count += pendingcnt [pri]; + + return count; +} + +void noinline +ev_invoke_pending (EV_P) +{ + pendingpri = NUMPRI; + + while (pendingpri) /* pendingpri possibly gets modified in the inner loop */ + { + --pendingpri; + + while (pendingcnt [pendingpri]) + { + ANPENDING *p = pendings [pendingpri] + --pendingcnt [pendingpri]; + + p->w->pending = 0; + EV_CB_INVOKE (p->w, p->events); + EV_FREQUENT_CHECK; + } + } +} + +#if EV_IDLE_ENABLE +/* make idle watchers pending. this handles the "call-idle */ +/* only when higher priorities are idle" logic */ +inline_size void +idle_reify (EV_P) +{ + if (expect_false (idleall)) + { + int pri; + + for (pri = NUMPRI; pri--; ) + { + if (pendingcnt [pri]) + break; + + if (idlecnt [pri]) + { + queue_events (EV_A_ (W *)idles [pri], idlecnt [pri], EV_IDLE); + break; + } + } + } +} +#endif + +/* make timers pending */ +inline_size void +timers_reify (EV_P) +{ + EV_FREQUENT_CHECK; + + if (timercnt && ANHE_at (timers [HEAP0]) < mn_now) + { + do + { + ev_timer *w = (ev_timer *)ANHE_w (timers [HEAP0]); + + /*assert (("libev: inactive timer on timer heap detected", ev_is_active (w)));*/ + + /* first reschedule or stop timer */ + if (w->repeat) + { + ev_at (w) += w->repeat; + if (ev_at (w) < mn_now) + ev_at (w) = mn_now; + + assert (("libev: negative ev_timer repeat value found while processing timers", w->repeat > 0.)); + + ANHE_at_cache (timers [HEAP0]); + downheap (timers, timercnt, HEAP0); + } + else + ev_timer_stop (EV_A_ w); /* nonrepeating: stop timer */ + + EV_FREQUENT_CHECK; + feed_reverse (EV_A_ (W)w); + } + while (timercnt && ANHE_at (timers [HEAP0]) < mn_now); + + feed_reverse_done (EV_A_ EV_TIMER); + } +} + +#if EV_PERIODIC_ENABLE + +static void noinline +periodic_recalc (EV_P_ ev_periodic *w) +{ + ev_tstamp interval = w->interval > MIN_INTERVAL ? w->interval : MIN_INTERVAL; + ev_tstamp at = w->offset + interval * ev_floor ((ev_rt_now - w->offset) / interval); + + /* the above almost always errs on the low side */ + while (at <= ev_rt_now) + { + ev_tstamp nat = at + w->interval; + + /* when resolution fails us, we use ev_rt_now */ + if (expect_false (nat == at)) + { + at = ev_rt_now; + break; + } + + at = nat; + } + + ev_at (w) = at; +} + +/* make periodics pending */ +inline_size void +periodics_reify (EV_P) +{ + EV_FREQUENT_CHECK; + + while (periodiccnt && ANHE_at (periodics [HEAP0]) < ev_rt_now) + { + do + { + ev_periodic *w = (ev_periodic *)ANHE_w (periodics [HEAP0]); + + /*assert (("libev: inactive timer on periodic heap detected", ev_is_active (w)));*/ + + /* first reschedule or stop timer */ + if (w->reschedule_cb) + { + ev_at (w) = w->reschedule_cb (w, ev_rt_now); + + assert (("libev: ev_periodic reschedule callback returned time in the past", ev_at (w) >= ev_rt_now)); + + ANHE_at_cache (periodics [HEAP0]); + downheap (periodics, periodiccnt, HEAP0); + } + else if (w->interval) + { + periodic_recalc (EV_A_ w); + ANHE_at_cache (periodics [HEAP0]); + downheap (periodics, periodiccnt, HEAP0); + } + else + ev_periodic_stop (EV_A_ w); /* nonrepeating: stop timer */ + + EV_FREQUENT_CHECK; + feed_reverse (EV_A_ (W)w); + } + while (periodiccnt && ANHE_at (periodics [HEAP0]) < ev_rt_now); + + feed_reverse_done (EV_A_ EV_PERIODIC); + } +} + +/* simply recalculate all periodics */ +/* TODO: maybe ensure that at least one event happens when jumping forward? */ +static void noinline ecb_cold +periodics_reschedule (EV_P) +{ + int i; + + /* adjust periodics after time jump */ + for (i = HEAP0; i < periodiccnt + HEAP0; ++i) + { + ev_periodic *w = (ev_periodic *)ANHE_w (periodics [i]); + + if (w->reschedule_cb) + ev_at (w) = w->reschedule_cb (w, ev_rt_now); + else if (w->interval) + periodic_recalc (EV_A_ w); + + ANHE_at_cache (periodics [i]); + } + + reheap (periodics, periodiccnt); +} +#endif + +/* adjust all timers by a given offset */ +static void noinline ecb_cold +timers_reschedule (EV_P_ ev_tstamp adjust) +{ + int i; + + for (i = 0; i < timercnt; ++i) + { + ANHE *he = timers + i + HEAP0; + ANHE_w (*he)->at += adjust; + ANHE_at_cache (*he); + } +} + +/* fetch new monotonic and realtime times from the kernel */ +/* also detect if there was a timejump, and act accordingly */ +inline_speed void +time_update (EV_P_ ev_tstamp max_block) +{ +#if EV_USE_MONOTONIC + if (expect_true (have_monotonic)) + { + int i; + ev_tstamp odiff = rtmn_diff; + + mn_now = get_clock (); + + /* only fetch the realtime clock every 0.5*MIN_TIMEJUMP seconds */ + /* interpolate in the meantime */ + if (expect_true (mn_now - now_floor < MIN_TIMEJUMP * .5)) + { + ev_rt_now = rtmn_diff + mn_now; + return; + } + + now_floor = mn_now; + ev_rt_now = ev_time (); + + /* loop a few times, before making important decisions. + * on the choice of "4": one iteration isn't enough, + * in case we get preempted during the calls to + * ev_time and get_clock. a second call is almost guaranteed + * to succeed in that case, though. and looping a few more times + * doesn't hurt either as we only do this on time-jumps or + * in the unlikely event of having been preempted here. + */ + for (i = 4; --i; ) + { + ev_tstamp diff; + rtmn_diff = ev_rt_now - mn_now; + + diff = odiff - rtmn_diff; + + if (expect_true ((diff < 0. ? -diff : diff) < MIN_TIMEJUMP)) + return; /* all is well */ + + ev_rt_now = ev_time (); + mn_now = get_clock (); + now_floor = mn_now; + } + + /* no timer adjustment, as the monotonic clock doesn't jump */ + /* timers_reschedule (EV_A_ rtmn_diff - odiff) */ +# if EV_PERIODIC_ENABLE + periodics_reschedule (EV_A); +# endif + } + else +#endif + { + ev_rt_now = ev_time (); + + if (expect_false (mn_now > ev_rt_now || ev_rt_now > mn_now + max_block + MIN_TIMEJUMP)) + { + /* adjust timers. this is easy, as the offset is the same for all of them */ + timers_reschedule (EV_A_ ev_rt_now - mn_now); +#if EV_PERIODIC_ENABLE + periodics_reschedule (EV_A); +#endif + } + + mn_now = ev_rt_now; + } +} + +int +ev_run (EV_P_ int flags) +{ +#if EV_FEATURE_API + ++loop_depth; +#endif + + assert (("libev: ev_loop recursion during release detected", loop_done != EVBREAK_RECURSE)); + + loop_done = EVBREAK_CANCEL; + + EV_INVOKE_PENDING; /* in case we recurse, ensure ordering stays nice and clean */ + + do + { +#if EV_VERIFY >= 2 + ev_verify (EV_A); +#endif + +#ifndef _WIN32 + if (expect_false (curpid)) /* penalise the forking check even more */ + if (expect_false (getpid () != curpid)) + { + curpid = getpid (); + postfork = 1; + } +#endif + +#if EV_FORK_ENABLE + /* we might have forked, so queue fork handlers */ + if (expect_false (postfork)) + if (forkcnt) + { + queue_events (EV_A_ (W *)forks, forkcnt, EV_FORK); + EV_INVOKE_PENDING; + } +#endif + +#if EV_PREPARE_ENABLE + /* queue prepare watchers (and execute them) */ + if (expect_false (preparecnt)) + { + queue_events (EV_A_ (W *)prepares, preparecnt, EV_PREPARE); + EV_INVOKE_PENDING; + } +#endif + + if (expect_false (loop_done)) + break; + + /* we might have forked, so reify kernel state if necessary */ + if (expect_false (postfork)) + loop_fork (EV_A); + + /* update fd-related kernel structures */ + fd_reify (EV_A); + + /* calculate blocking time */ + { + ev_tstamp waittime = 0.; + ev_tstamp sleeptime = 0.; + + /* remember old timestamp for io_blocktime calculation */ + ev_tstamp prev_mn_now = mn_now; + + /* update time to cancel out callback processing overhead */ + time_update (EV_A_ 1e100); + + /* from now on, we want a pipe-wake-up */ + pipe_write_wanted = 1; + + ECB_MEMORY_FENCE; /* make sure pipe_write_wanted is visible before we check for potential skips */ + + if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt || pipe_write_skipped))) + { + waittime = MAX_BLOCKTIME; + + if (timercnt) + { + ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now; + if (waittime > to) waittime = to; + } + +#if EV_PERIODIC_ENABLE + if (periodiccnt) + { + ev_tstamp to = ANHE_at (periodics [HEAP0]) - ev_rt_now; + if (waittime > to) waittime = to; + } +#endif + + /* don't let timeouts decrease the waittime below timeout_blocktime */ + if (expect_false (waittime < timeout_blocktime)) + waittime = timeout_blocktime; + + /* at this point, we NEED to wait, so we have to ensure */ + /* to pass a minimum nonzero value to the backend */ + if (expect_false (waittime < backend_mintime)) + waittime = backend_mintime; + + /* extra check because io_blocktime is commonly 0 */ + if (expect_false (io_blocktime)) + { + sleeptime = io_blocktime - (mn_now - prev_mn_now); + + if (sleeptime > waittime - backend_mintime) + sleeptime = waittime - backend_mintime; + + if (expect_true (sleeptime > 0.)) + { + ev_sleep (sleeptime); + waittime -= sleeptime; + } + } + } + +#if EV_FEATURE_API + ++loop_count; +#endif + assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */ + backend_poll (EV_A_ waittime); + assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */ + + pipe_write_wanted = 0; /* just an optimisation, no fence needed */ + + ECB_MEMORY_FENCE_ACQUIRE; + if (pipe_write_skipped) + { + assert (("libev: pipe_w not active, but pipe not written", ev_is_active (&pipe_w))); + ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM); + } + + + /* update ev_rt_now, do magic */ + time_update (EV_A_ waittime + sleeptime); + } + + /* queue pending timers and reschedule them */ + timers_reify (EV_A); /* relative timers called last */ +#if EV_PERIODIC_ENABLE + periodics_reify (EV_A); /* absolute timers called first */ +#endif + +#if EV_IDLE_ENABLE + /* queue idle watchers unless other events are pending */ + idle_reify (EV_A); +#endif + +#if EV_CHECK_ENABLE + /* queue check watchers, to be executed first */ + if (expect_false (checkcnt)) + queue_events (EV_A_ (W *)checks, checkcnt, EV_CHECK); +#endif + + EV_INVOKE_PENDING; + } + while (expect_true ( + activecnt + && !loop_done + && !(flags & (EVRUN_ONCE | EVRUN_NOWAIT)) + )); + + if (loop_done == EVBREAK_ONE) + loop_done = EVBREAK_CANCEL; + +#if EV_FEATURE_API + --loop_depth; +#endif + + return activecnt; +} + +void +ev_break (EV_P_ int how) EV_THROW +{ + loop_done = how; +} + +void +ev_ref (EV_P) EV_THROW +{ + ++activecnt; +} + +void +ev_unref (EV_P) EV_THROW +{ + --activecnt; +} + +void +ev_now_update (EV_P) EV_THROW +{ + time_update (EV_A_ 1e100); +} + +void +ev_suspend (EV_P) EV_THROW +{ + ev_now_update (EV_A); +} + +void +ev_resume (EV_P) EV_THROW +{ + ev_tstamp mn_prev = mn_now; + + ev_now_update (EV_A); + timers_reschedule (EV_A_ mn_now - mn_prev); +#if EV_PERIODIC_ENABLE + /* TODO: really do this? */ + periodics_reschedule (EV_A); +#endif +} + +/*****************************************************************************/ +/* singly-linked list management, used when the expected list length is short */ + +inline_size void +wlist_add (WL *head, WL elem) +{ + elem->next = *head; + *head = elem; +} + +inline_size void +wlist_del (WL *head, WL elem) +{ + while (*head) + { + if (expect_true (*head == elem)) + { + *head = elem->next; + break; + } + + head = &(*head)->next; + } +} + +/* internal, faster, version of ev_clear_pending */ +inline_speed void +clear_pending (EV_P_ W w) +{ + if (w->pending) + { + pendings [ABSPRI (w)][w->pending - 1].w = (W)&pending_w; + w->pending = 0; + } +} + +int +ev_clear_pending (EV_P_ void *w) EV_THROW +{ + W w_ = (W)w; + int pending = w_->pending; + + if (expect_true (pending)) + { + ANPENDING *p = pendings [ABSPRI (w_)] + pending - 1; + p->w = (W)&pending_w; + w_->pending = 0; + return p->events; + } + else + return 0; +} + +inline_size void +pri_adjust (EV_P_ W w) +{ + int pri = ev_priority (w); + pri = pri < EV_MINPRI ? EV_MINPRI : pri; + pri = pri > EV_MAXPRI ? EV_MAXPRI : pri; + ev_set_priority (w, pri); +} + +inline_speed void +ev_start (EV_P_ W w, int active) +{ + pri_adjust (EV_A_ w); + w->active = active; + ev_ref (EV_A); +} + +inline_size void +ev_stop (EV_P_ W w) +{ + ev_unref (EV_A); + w->active = 0; +} + +/*****************************************************************************/ + +void noinline +ev_io_start (EV_P_ ev_io *w) EV_THROW +{ + int fd = w->fd; + + if (expect_false (ev_is_active (w))) + return; + + assert (("libev: ev_io_start called with negative fd", fd >= 0)); + assert (("libev: ev_io_start called with illegal event mask", !(w->events & ~(EV__IOFDSET | EV_READ | EV_WRITE)))); + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, 1); + array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero); + wlist_add (&anfds[fd].head, (WL)w); + + /* common bug, apparently */ + assert (("libev: ev_io_start called with corrupted watcher", ((WL)w)->next != (WL)w)); + + fd_change (EV_A_ fd, (w->events & EV__IOFDSET) | EV_ANFD_REIFY); + w->events &= ~EV__IOFDSET; + + EV_FREQUENT_CHECK; +} + +void noinline +ev_io_stop (EV_P_ ev_io *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + assert (("libev: ev_io_stop called with illegal fd (must stay constant after start!)", w->fd >= 0 && w->fd < anfdmax)); + + EV_FREQUENT_CHECK; + + wlist_del (&anfds[w->fd].head, (WL)w); + ev_stop (EV_A_ (W)w); + + fd_change (EV_A_ w->fd, EV_ANFD_REIFY); + + EV_FREQUENT_CHECK; +} + +void noinline +ev_timer_start (EV_P_ ev_timer *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + ev_at (w) += mn_now; + + assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.)); + + EV_FREQUENT_CHECK; + + ++timercnt; + ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1); + array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2); + ANHE_w (timers [ev_active (w)]) = (WT)w; + ANHE_at_cache (timers [ev_active (w)]); + upheap (timers, ev_active (w)); + + EV_FREQUENT_CHECK; + + /*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/ +} + +void noinline +ev_timer_stop (EV_P_ ev_timer *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + assert (("libev: internal timer heap corruption", ANHE_w (timers [active]) == (WT)w)); + + --timercnt; + + if (expect_true (active < timercnt + HEAP0)) + { + timers [active] = timers [timercnt + HEAP0]; + adjustheap (timers, timercnt, active); + } + } + + ev_at (w) -= mn_now; + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} + +void noinline +ev_timer_again (EV_P_ ev_timer *w) EV_THROW +{ + EV_FREQUENT_CHECK; + + clear_pending (EV_A_ (W)w); + + if (ev_is_active (w)) + { + if (w->repeat) + { + ev_at (w) = mn_now + w->repeat; + ANHE_at_cache (timers [ev_active (w)]); + adjustheap (timers, timercnt, ev_active (w)); + } + else + ev_timer_stop (EV_A_ w); + } + else if (w->repeat) + { + ev_at (w) = w->repeat; + ev_timer_start (EV_A_ w); + } + + EV_FREQUENT_CHECK; +} + +ev_tstamp +ev_timer_remaining (EV_P_ ev_timer *w) EV_THROW +{ + return ev_at (w) - (ev_is_active (w) ? mn_now : 0.); +} + +#if EV_PERIODIC_ENABLE +void noinline +ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + if (w->reschedule_cb) + ev_at (w) = w->reschedule_cb (w, ev_rt_now); + else if (w->interval) + { + assert (("libev: ev_periodic_start called with negative interval value", w->interval >= 0.)); + periodic_recalc (EV_A_ w); + } + else + ev_at (w) = w->offset; + + EV_FREQUENT_CHECK; + + ++periodiccnt; + ev_start (EV_A_ (W)w, periodiccnt + HEAP0 - 1); + array_needsize (ANHE, periodics, periodicmax, ev_active (w) + 1, EMPTY2); + ANHE_w (periodics [ev_active (w)]) = (WT)w; + ANHE_at_cache (periodics [ev_active (w)]); + upheap (periodics, ev_active (w)); + + EV_FREQUENT_CHECK; + + /*assert (("libev: internal periodic heap corruption", ANHE_w (periodics [ev_active (w)]) == (WT)w));*/ +} + +void noinline +ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + assert (("libev: internal periodic heap corruption", ANHE_w (periodics [active]) == (WT)w)); + + --periodiccnt; + + if (expect_true (active < periodiccnt + HEAP0)) + { + periodics [active] = periodics [periodiccnt + HEAP0]; + adjustheap (periodics, periodiccnt, active); + } + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} + +void noinline +ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW +{ + /* TODO: use adjustheap and recalculation */ + ev_periodic_stop (EV_A_ w); + ev_periodic_start (EV_A_ w); +} +#endif + +#ifndef SA_RESTART +# define SA_RESTART 0 +#endif + +#if EV_SIGNAL_ENABLE + +void noinline +ev_signal_start (EV_P_ ev_signal *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + assert (("libev: ev_signal_start called with illegal signal number", w->signum > 0 && w->signum < EV_NSIG)); + +#if EV_MULTIPLICITY + assert (("libev: a signal must not be attached to two different loops", + !signals [w->signum - 1].loop || signals [w->signum - 1].loop == loop)); + + signals [w->signum - 1].loop = EV_A; + ECB_MEMORY_FENCE_RELEASE; +#endif + + EV_FREQUENT_CHECK; + +#if EV_USE_SIGNALFD + if (sigfd == -2) + { + sigfd = signalfd (-1, &sigfd_set, SFD_NONBLOCK | SFD_CLOEXEC); + if (sigfd < 0 && errno == EINVAL) + sigfd = signalfd (-1, &sigfd_set, 0); /* retry without flags */ + + if (sigfd >= 0) + { + fd_intern (sigfd); /* doing it twice will not hurt */ + + sigemptyset (&sigfd_set); + + ev_io_init (&sigfd_w, sigfdcb, sigfd, EV_READ); + ev_set_priority (&sigfd_w, EV_MAXPRI); + ev_io_start (EV_A_ &sigfd_w); + ev_unref (EV_A); /* signalfd watcher should not keep loop alive */ + } + } + + if (sigfd >= 0) + { + /* TODO: check .head */ + sigaddset (&sigfd_set, w->signum); + sigprocmask (SIG_BLOCK, &sigfd_set, 0); + + signalfd (sigfd, &sigfd_set, 0); + } +#endif + + ev_start (EV_A_ (W)w, 1); + wlist_add (&signals [w->signum - 1].head, (WL)w); + + if (!((WL)w)->next) +# if EV_USE_SIGNALFD + if (sigfd < 0) /*TODO*/ +# endif + { +# ifdef _WIN32 + evpipe_init (EV_A); + + signal (w->signum, ev_sighandler); +# else + struct sigaction sa; + + evpipe_init (EV_A); + + sa.sa_handler = ev_sighandler; + sigfillset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; /* if restarting works we save one iteration */ + sigaction (w->signum, &sa, 0); + + if (origflags & EVFLAG_NOSIGMASK) + { + sigemptyset (&sa.sa_mask); + sigaddset (&sa.sa_mask, w->signum); + sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0); + } +#endif + } + + EV_FREQUENT_CHECK; +} + +void noinline +ev_signal_stop (EV_P_ ev_signal *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + wlist_del (&signals [w->signum - 1].head, (WL)w); + ev_stop (EV_A_ (W)w); + + if (!signals [w->signum - 1].head) + { +#if EV_MULTIPLICITY + signals [w->signum - 1].loop = 0; /* unattach from signal */ +#endif +#if EV_USE_SIGNALFD + if (sigfd >= 0) + { + sigset_t ss; + + sigemptyset (&ss); + sigaddset (&ss, w->signum); + sigdelset (&sigfd_set, w->signum); + + signalfd (sigfd, &sigfd_set, 0); + sigprocmask (SIG_UNBLOCK, &ss, 0); + } + else +#endif + signal (w->signum, SIG_DFL); + } + + EV_FREQUENT_CHECK; +} + +#endif + +#if EV_CHILD_ENABLE + +void +ev_child_start (EV_P_ ev_child *w) EV_THROW +{ +#if EV_MULTIPLICITY + assert (("libev: child watchers are only supported in the default loop", loop == ev_default_loop_ptr)); +#endif + if (expect_false (ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, 1); + wlist_add (&childs [w->pid & ((EV_PID_HASHSIZE) - 1)], (WL)w); + + EV_FREQUENT_CHECK; +} + +void +ev_child_stop (EV_P_ ev_child *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + wlist_del (&childs [w->pid & ((EV_PID_HASHSIZE) - 1)], (WL)w); + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} + +#endif + +#if EV_STAT_ENABLE + +# ifdef _WIN32 +# undef lstat +# define lstat(a,b) _stati64 (a,b) +# endif + +#define DEF_STAT_INTERVAL 5.0074891 +#define NFS_STAT_INTERVAL 30.1074891 /* for filesystems potentially failing inotify */ +#define MIN_STAT_INTERVAL 0.1074891 + +static void noinline stat_timer_cb (EV_P_ ev_timer *w_, int revents); + +#if EV_USE_INOTIFY + +/* the * 2 is to allow for alignment padding, which for some reason is >> 8 */ +# define EV_INOTIFY_BUFSIZE (sizeof (struct inotify_event) * 2 + NAME_MAX) + +static void noinline +infy_add (EV_P_ ev_stat *w) +{ + w->wd = inotify_add_watch (fs_fd, w->path, + IN_ATTRIB | IN_DELETE_SELF | IN_MOVE_SELF | IN_MODIFY + | IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO + | IN_DONT_FOLLOW | IN_MASK_ADD); + + if (w->wd >= 0) + { + struct statfs sfs; + + /* now local changes will be tracked by inotify, but remote changes won't */ + /* unless the filesystem is known to be local, we therefore still poll */ + /* also do poll on <2.6.25, but with normal frequency */ + + if (!fs_2625) + w->timer.repeat = w->interval ? w->interval : DEF_STAT_INTERVAL; + else if (!statfs (w->path, &sfs) + && (sfs.f_type == 0x1373 /* devfs */ + || sfs.f_type == 0x4006 /* fat */ + || sfs.f_type == 0x4d44 /* msdos */ + || sfs.f_type == 0xEF53 /* ext2/3 */ + || sfs.f_type == 0x72b6 /* jffs2 */ + || sfs.f_type == 0x858458f6 /* ramfs */ + || sfs.f_type == 0x5346544e /* ntfs */ + || sfs.f_type == 0x3153464a /* jfs */ + || sfs.f_type == 0x9123683e /* btrfs */ + || sfs.f_type == 0x52654973 /* reiser3 */ + || sfs.f_type == 0x01021994 /* tmpfs */ + || sfs.f_type == 0x58465342 /* xfs */)) + w->timer.repeat = 0.; /* filesystem is local, kernel new enough */ + else + w->timer.repeat = w->interval ? w->interval : NFS_STAT_INTERVAL; /* remote, use reduced frequency */ + } + else + { + /* can't use inotify, continue to stat */ + w->timer.repeat = w->interval ? w->interval : DEF_STAT_INTERVAL; + + /* if path is not there, monitor some parent directory for speedup hints */ + /* note that exceeding the hardcoded path limit is not a correctness issue, */ + /* but an efficiency issue only */ + if ((errno == ENOENT || errno == EACCES) && strlen (w->path) < 4096) + { + char path [4096]; + strcpy (path, w->path); + + do + { + int mask = IN_MASK_ADD | IN_DELETE_SELF | IN_MOVE_SELF + | (errno == EACCES ? IN_ATTRIB : IN_CREATE | IN_MOVED_TO); + + char *pend = strrchr (path, '/'); + + if (!pend || pend == path) + break; + + *pend = 0; + w->wd = inotify_add_watch (fs_fd, path, mask); + } + while (w->wd < 0 && (errno == ENOENT || errno == EACCES)); + } + } + + if (w->wd >= 0) + wlist_add (&fs_hash [w->wd & ((EV_INOTIFY_HASHSIZE) - 1)].head, (WL)w); + + /* now re-arm timer, if required */ + if (ev_is_active (&w->timer)) ev_ref (EV_A); + ev_timer_again (EV_A_ &w->timer); + if (ev_is_active (&w->timer)) ev_unref (EV_A); +} + +static void noinline +infy_del (EV_P_ ev_stat *w) +{ + int slot; + int wd = w->wd; + + if (wd < 0) + return; + + w->wd = -2; + slot = wd & ((EV_INOTIFY_HASHSIZE) - 1); + wlist_del (&fs_hash [slot].head, (WL)w); + + /* remove this watcher, if others are watching it, they will rearm */ + inotify_rm_watch (fs_fd, wd); +} + +static void noinline +infy_wd (EV_P_ int slot, int wd, struct inotify_event *ev) +{ + if (slot < 0) + /* overflow, need to check for all hash slots */ + for (slot = 0; slot < (EV_INOTIFY_HASHSIZE); ++slot) + infy_wd (EV_A_ slot, wd, ev); + else + { + WL w_; + + for (w_ = fs_hash [slot & ((EV_INOTIFY_HASHSIZE) - 1)].head; w_; ) + { + ev_stat *w = (ev_stat *)w_; + w_ = w_->next; /* lets us remove this watcher and all before it */ + + if (w->wd == wd || wd == -1) + { + if (ev->mask & (IN_IGNORED | IN_UNMOUNT | IN_DELETE_SELF)) + { + wlist_del (&fs_hash [slot & ((EV_INOTIFY_HASHSIZE) - 1)].head, (WL)w); + w->wd = -1; + infy_add (EV_A_ w); /* re-add, no matter what */ + } + + stat_timer_cb (EV_A_ &w->timer, 0); + } + } + } +} + +static void +infy_cb (EV_P_ ev_io *w, int revents) +{ + char buf [EV_INOTIFY_BUFSIZE]; + int ofs; + int len = read (fs_fd, buf, sizeof (buf)); + + for (ofs = 0; ofs < len; ) + { + struct inotify_event *ev = (struct inotify_event *)(buf + ofs); + infy_wd (EV_A_ ev->wd, ev->wd, ev); + ofs += sizeof (struct inotify_event) + ev->len; + } +} + +inline_size void ecb_cold +ev_check_2625 (EV_P) +{ + /* kernels < 2.6.25 are borked + * http://www.ussg.indiana.edu/hypermail/linux/kernel/0711.3/1208.html + */ + if (ev_linux_version () < 0x020619) + return; + + fs_2625 = 1; +} + +inline_size int +infy_newfd (void) +{ +#if defined IN_CLOEXEC && defined IN_NONBLOCK + int fd = inotify_init1 (IN_CLOEXEC | IN_NONBLOCK); + if (fd >= 0) + return fd; +#endif + return inotify_init (); +} + +inline_size void +infy_init (EV_P) +{ + if (fs_fd != -2) + return; + + fs_fd = -1; + + ev_check_2625 (EV_A); + + fs_fd = infy_newfd (); + + if (fs_fd >= 0) + { + fd_intern (fs_fd); + ev_io_init (&fs_w, infy_cb, fs_fd, EV_READ); + ev_set_priority (&fs_w, EV_MAXPRI); + ev_io_start (EV_A_ &fs_w); + ev_unref (EV_A); + } +} + +inline_size void +infy_fork (EV_P) +{ + int slot; + + if (fs_fd < 0) + return; + + ev_ref (EV_A); + ev_io_stop (EV_A_ &fs_w); + close (fs_fd); + fs_fd = infy_newfd (); + + if (fs_fd >= 0) + { + fd_intern (fs_fd); + ev_io_set (&fs_w, fs_fd, EV_READ); + ev_io_start (EV_A_ &fs_w); + ev_unref (EV_A); + } + + for (slot = 0; slot < (EV_INOTIFY_HASHSIZE); ++slot) + { + WL w_ = fs_hash [slot].head; + fs_hash [slot].head = 0; + + while (w_) + { + ev_stat *w = (ev_stat *)w_; + w_ = w_->next; /* lets us add this watcher */ + + w->wd = -1; + + if (fs_fd >= 0) + infy_add (EV_A_ w); /* re-add, no matter what */ + else + { + w->timer.repeat = w->interval ? w->interval : DEF_STAT_INTERVAL; + if (ev_is_active (&w->timer)) ev_ref (EV_A); + ev_timer_again (EV_A_ &w->timer); + if (ev_is_active (&w->timer)) ev_unref (EV_A); + } + } + } +} + +#endif + +#ifdef _WIN32 +# define EV_LSTAT(p,b) _stati64 (p, b) +#else +# define EV_LSTAT(p,b) lstat (p, b) +#endif + +void +ev_stat_stat (EV_P_ ev_stat *w) EV_THROW +{ + if (lstat (w->path, &w->attr) < 0) + w->attr.st_nlink = 0; + else if (!w->attr.st_nlink) + w->attr.st_nlink = 1; +} + +static void noinline +stat_timer_cb (EV_P_ ev_timer *w_, int revents) +{ + ev_stat *w = (ev_stat *)(((char *)w_) - offsetof (ev_stat, timer)); + + ev_statdata prev = w->attr; + ev_stat_stat (EV_A_ w); + + /* memcmp doesn't work on netbsd, they.... do stuff to their struct stat */ + if ( + prev.st_dev != w->attr.st_dev + || prev.st_ino != w->attr.st_ino + || prev.st_mode != w->attr.st_mode + || prev.st_nlink != w->attr.st_nlink + || prev.st_uid != w->attr.st_uid + || prev.st_gid != w->attr.st_gid + || prev.st_rdev != w->attr.st_rdev + || prev.st_size != w->attr.st_size + || prev.st_atime != w->attr.st_atime + || prev.st_mtime != w->attr.st_mtime + || prev.st_ctime != w->attr.st_ctime + ) { + /* we only update w->prev on actual differences */ + /* in case we test more often than invoke the callback, */ + /* to ensure that prev is always different to attr */ + w->prev = prev; + + #if EV_USE_INOTIFY + if (fs_fd >= 0) + { + infy_del (EV_A_ w); + infy_add (EV_A_ w); + ev_stat_stat (EV_A_ w); /* avoid race... */ + } + #endif + + ev_feed_event (EV_A_ w, EV_STAT); + } +} + +void +ev_stat_start (EV_P_ ev_stat *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + ev_stat_stat (EV_A_ w); + + if (w->interval < MIN_STAT_INTERVAL && w->interval) + w->interval = MIN_STAT_INTERVAL; + + ev_timer_init (&w->timer, stat_timer_cb, 0., w->interval ? w->interval : DEF_STAT_INTERVAL); + ev_set_priority (&w->timer, ev_priority (w)); + +#if EV_USE_INOTIFY + infy_init (EV_A); + + if (fs_fd >= 0) + infy_add (EV_A_ w); + else +#endif + { + ev_timer_again (EV_A_ &w->timer); + ev_unref (EV_A); + } + + ev_start (EV_A_ (W)w, 1); + + EV_FREQUENT_CHECK; +} + +void +ev_stat_stop (EV_P_ ev_stat *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + +#if EV_USE_INOTIFY + infy_del (EV_A_ w); +#endif + + if (ev_is_active (&w->timer)) + { + ev_ref (EV_A); + ev_timer_stop (EV_A_ &w->timer); + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_IDLE_ENABLE +void +ev_idle_start (EV_P_ ev_idle *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + pri_adjust (EV_A_ (W)w); + + EV_FREQUENT_CHECK; + + { + int active = ++idlecnt [ABSPRI (w)]; + + ++idleall; + ev_start (EV_A_ (W)w, active); + + array_needsize (ev_idle *, idles [ABSPRI (w)], idlemax [ABSPRI (w)], active, EMPTY2); + idles [ABSPRI (w)][active - 1] = w; + } + + EV_FREQUENT_CHECK; +} + +void +ev_idle_stop (EV_P_ ev_idle *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + idles [ABSPRI (w)][active - 1] = idles [ABSPRI (w)][--idlecnt [ABSPRI (w)]]; + ev_active (idles [ABSPRI (w)][active - 1]) = active; + + ev_stop (EV_A_ (W)w); + --idleall; + } + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_PREPARE_ENABLE +void +ev_prepare_start (EV_P_ ev_prepare *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, ++preparecnt); + array_needsize (ev_prepare *, prepares, preparemax, preparecnt, EMPTY2); + prepares [preparecnt - 1] = w; + + EV_FREQUENT_CHECK; +} + +void +ev_prepare_stop (EV_P_ ev_prepare *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + prepares [active - 1] = prepares [--preparecnt]; + ev_active (prepares [active - 1]) = active; + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_CHECK_ENABLE +void +ev_check_start (EV_P_ ev_check *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, ++checkcnt); + array_needsize (ev_check *, checks, checkmax, checkcnt, EMPTY2); + checks [checkcnt - 1] = w; + + EV_FREQUENT_CHECK; +} + +void +ev_check_stop (EV_P_ ev_check *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + checks [active - 1] = checks [--checkcnt]; + ev_active (checks [active - 1]) = active; + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_EMBED_ENABLE +void noinline +ev_embed_sweep (EV_P_ ev_embed *w) EV_THROW +{ + ev_run (w->other, EVRUN_NOWAIT); +} + +static void +embed_io_cb (EV_P_ ev_io *io, int revents) +{ + ev_embed *w = (ev_embed *)(((char *)io) - offsetof (ev_embed, io)); + + if (ev_cb (w)) + ev_feed_event (EV_A_ (W)w, EV_EMBED); + else + ev_run (w->other, EVRUN_NOWAIT); +} + +static void +embed_prepare_cb (EV_P_ ev_prepare *prepare, int revents) +{ + ev_embed *w = (ev_embed *)(((char *)prepare) - offsetof (ev_embed, prepare)); + + { + EV_P = w->other; + + while (fdchangecnt) + { + fd_reify (EV_A); + ev_run (EV_A_ EVRUN_NOWAIT); + } + } +} + +static void +embed_fork_cb (EV_P_ ev_fork *fork_w, int revents) +{ + ev_embed *w = (ev_embed *)(((char *)fork_w) - offsetof (ev_embed, fork)); + + ev_embed_stop (EV_A_ w); + + { + EV_P = w->other; + + ev_loop_fork (EV_A); + ev_run (EV_A_ EVRUN_NOWAIT); + } + + ev_embed_start (EV_A_ w); +} + +#if 0 +static void +embed_idle_cb (EV_P_ ev_idle *idle, int revents) +{ + ev_idle_stop (EV_A_ idle); +} +#endif + +void +ev_embed_start (EV_P_ ev_embed *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + { + EV_P = w->other; + assert (("libev: loop to be embedded is not embeddable", backend & ev_embeddable_backends ())); + ev_io_init (&w->io, embed_io_cb, backend_fd, EV_READ); + } + + EV_FREQUENT_CHECK; + + ev_set_priority (&w->io, ev_priority (w)); + ev_io_start (EV_A_ &w->io); + + ev_prepare_init (&w->prepare, embed_prepare_cb); + ev_set_priority (&w->prepare, EV_MINPRI); + ev_prepare_start (EV_A_ &w->prepare); + + ev_fork_init (&w->fork, embed_fork_cb); + ev_fork_start (EV_A_ &w->fork); + + /*ev_idle_init (&w->idle, e,bed_idle_cb);*/ + + ev_start (EV_A_ (W)w, 1); + + EV_FREQUENT_CHECK; +} + +void +ev_embed_stop (EV_P_ ev_embed *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + ev_io_stop (EV_A_ &w->io); + ev_prepare_stop (EV_A_ &w->prepare); + ev_fork_stop (EV_A_ &w->fork); + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_FORK_ENABLE +void +ev_fork_start (EV_P_ ev_fork *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, ++forkcnt); + array_needsize (ev_fork *, forks, forkmax, forkcnt, EMPTY2); + forks [forkcnt - 1] = w; + + EV_FREQUENT_CHECK; +} + +void +ev_fork_stop (EV_P_ ev_fork *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + forks [active - 1] = forks [--forkcnt]; + ev_active (forks [active - 1]) = active; + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_CLEANUP_ENABLE +void +ev_cleanup_start (EV_P_ ev_cleanup *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, ++cleanupcnt); + array_needsize (ev_cleanup *, cleanups, cleanupmax, cleanupcnt, EMPTY2); + cleanups [cleanupcnt - 1] = w; + + /* cleanup watchers should never keep a refcount on the loop */ + ev_unref (EV_A); + EV_FREQUENT_CHECK; +} + +void +ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + ev_ref (EV_A); + + { + int active = ev_active (w); + + cleanups [active - 1] = cleanups [--cleanupcnt]; + ev_active (cleanups [active - 1]) = active; + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} +#endif + +#if EV_ASYNC_ENABLE +void +ev_async_start (EV_P_ ev_async *w) EV_THROW +{ + if (expect_false (ev_is_active (w))) + return; + + w->sent = 0; + + evpipe_init (EV_A); + + EV_FREQUENT_CHECK; + + ev_start (EV_A_ (W)w, ++asynccnt); + array_needsize (ev_async *, asyncs, asyncmax, asynccnt, EMPTY2); + asyncs [asynccnt - 1] = w; + + EV_FREQUENT_CHECK; +} + +void +ev_async_stop (EV_P_ ev_async *w) EV_THROW +{ + clear_pending (EV_A_ (W)w); + if (expect_false (!ev_is_active (w))) + return; + + EV_FREQUENT_CHECK; + + { + int active = ev_active (w); + + asyncs [active - 1] = asyncs [--asynccnt]; + ev_active (asyncs [active - 1]) = active; + } + + ev_stop (EV_A_ (W)w); + + EV_FREQUENT_CHECK; +} + +void +ev_async_send (EV_P_ ev_async *w) EV_THROW +{ + w->sent = 1; + evpipe_write (EV_A_ &async_pending); +} +#endif + +/*****************************************************************************/ + +struct ev_once +{ + ev_io io; + ev_timer to; + void (*cb)(int revents, void *arg); + void *arg; +}; + +static void +once_cb (EV_P_ struct ev_once *once, int revents) +{ + void (*cb)(int revents, void *arg) = once->cb; + void *arg = once->arg; + + ev_io_stop (EV_A_ &once->io); + ev_timer_stop (EV_A_ &once->to); + ev_free (once); + + cb (revents, arg); +} + +static void +once_cb_io (EV_P_ ev_io *w, int revents) +{ + struct ev_once *once = (struct ev_once *)(((char *)w) - offsetof (struct ev_once, io)); + + once_cb (EV_A_ once, revents | ev_clear_pending (EV_A_ &once->to)); +} + +static void +once_cb_to (EV_P_ ev_timer *w, int revents) +{ + struct ev_once *once = (struct ev_once *)(((char *)w) - offsetof (struct ev_once, to)); + + once_cb (EV_A_ once, revents | ev_clear_pending (EV_A_ &once->io)); +} + +void +ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg) EV_THROW +{ + struct ev_once *once = (struct ev_once *)ev_malloc (sizeof (struct ev_once)); + + if (expect_false (!once)) + { + cb (EV_ERROR | EV_READ | EV_WRITE | EV_TIMER, arg); + return; + } + + once->cb = cb; + once->arg = arg; + + ev_init (&once->io, once_cb_io); + if (fd >= 0) + { + ev_io_set (&once->io, fd, events); + ev_io_start (EV_A_ &once->io); + } + + ev_init (&once->to, once_cb_to); + if (timeout >= 0.) + { + ev_timer_set (&once->to, timeout, 0.); + ev_timer_start (EV_A_ &once->to); + } +} + +/*****************************************************************************/ + +#if EV_WALK_ENABLE +void ecb_cold +ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_THROW +{ + int i, j; + ev_watcher_list *wl, *wn; + + if (types & (EV_IO | EV_EMBED)) + for (i = 0; i < anfdmax; ++i) + for (wl = anfds [i].head; wl; ) + { + wn = wl->next; + +#if EV_EMBED_ENABLE + if (ev_cb ((ev_io *)wl) == embed_io_cb) + { + if (types & EV_EMBED) + cb (EV_A_ EV_EMBED, ((char *)wl) - offsetof (struct ev_embed, io)); + } + else +#endif +#if EV_USE_INOTIFY + if (ev_cb ((ev_io *)wl) == infy_cb) + ; + else +#endif + if ((ev_io *)wl != &pipe_w) + if (types & EV_IO) + cb (EV_A_ EV_IO, wl); + + wl = wn; + } + + if (types & (EV_TIMER | EV_STAT)) + for (i = timercnt + HEAP0; i-- > HEAP0; ) +#if EV_STAT_ENABLE + /*TODO: timer is not always active*/ + if (ev_cb ((ev_timer *)ANHE_w (timers [i])) == stat_timer_cb) + { + if (types & EV_STAT) + cb (EV_A_ EV_STAT, ((char *)ANHE_w (timers [i])) - offsetof (struct ev_stat, timer)); + } + else +#endif + if (types & EV_TIMER) + cb (EV_A_ EV_TIMER, ANHE_w (timers [i])); + +#if EV_PERIODIC_ENABLE + if (types & EV_PERIODIC) + for (i = periodiccnt + HEAP0; i-- > HEAP0; ) + cb (EV_A_ EV_PERIODIC, ANHE_w (periodics [i])); +#endif + +#if EV_IDLE_ENABLE + if (types & EV_IDLE) + for (j = NUMPRI; j--; ) + for (i = idlecnt [j]; i--; ) + cb (EV_A_ EV_IDLE, idles [j][i]); +#endif + +#if EV_FORK_ENABLE + if (types & EV_FORK) + for (i = forkcnt; i--; ) + if (ev_cb (forks [i]) != embed_fork_cb) + cb (EV_A_ EV_FORK, forks [i]); +#endif + +#if EV_ASYNC_ENABLE + if (types & EV_ASYNC) + for (i = asynccnt; i--; ) + cb (EV_A_ EV_ASYNC, asyncs [i]); +#endif + +#if EV_PREPARE_ENABLE + if (types & EV_PREPARE) + for (i = preparecnt; i--; ) +# if EV_EMBED_ENABLE + if (ev_cb (prepares [i]) != embed_prepare_cb) +# endif + cb (EV_A_ EV_PREPARE, prepares [i]); +#endif + +#if EV_CHECK_ENABLE + if (types & EV_CHECK) + for (i = checkcnt; i--; ) + cb (EV_A_ EV_CHECK, checks [i]); +#endif + +#if EV_SIGNAL_ENABLE + if (types & EV_SIGNAL) + for (i = 0; i < EV_NSIG - 1; ++i) + for (wl = signals [i].head; wl; ) + { + wn = wl->next; + cb (EV_A_ EV_SIGNAL, wl); + wl = wn; + } +#endif + +#if EV_CHILD_ENABLE + if (types & EV_CHILD) + for (i = (EV_PID_HASHSIZE); i--; ) + for (wl = childs [i]; wl; ) + { + wn = wl->next; + cb (EV_A_ EV_CHILD, wl); + wl = wn; + } +#endif +/* EV_STAT 0x00001000 * stat data changed */ +/* EV_EMBED 0x00010000 * embedded event loop needs sweep */ +} +#endif + +#if EV_MULTIPLICITY + #include "ev_wrap.h" +#endif + diff --git a/framework/src/audit/src/libev/ev.h b/framework/src/audit/src/libev/ev.h new file mode 100644 index 00000000..7c6e7eb8 --- /dev/null +++ b/framework/src/audit/src/libev/ev.h @@ -0,0 +1,854 @@ +/* + * libev native API header + * + * Copyright (c) 2007,2008,2009,2010,2011,2012,2015 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#ifndef EV_H_ +#define EV_H_ + +#ifdef __cplusplus +# define EV_CPP(x) x +# if __cplusplus >= 201103L +# define EV_THROW noexcept +# else +# define EV_THROW throw () +# endif +#else +# define EV_CPP(x) +# define EV_THROW +#endif + +EV_CPP(extern "C" {) + +/*****************************************************************************/ + +/* pre-4.0 compatibility */ +#ifndef EV_COMPAT3 +# define EV_COMPAT3 1 +#endif + +#ifndef EV_FEATURES +# if defined __OPTIMIZE_SIZE__ +# define EV_FEATURES 0x7c +# else +# define EV_FEATURES 0x7f +# endif +#endif + +#define EV_FEATURE_CODE ((EV_FEATURES) & 1) +#define EV_FEATURE_DATA ((EV_FEATURES) & 2) +#define EV_FEATURE_CONFIG ((EV_FEATURES) & 4) +#define EV_FEATURE_API ((EV_FEATURES) & 8) +#define EV_FEATURE_WATCHERS ((EV_FEATURES) & 16) +#define EV_FEATURE_BACKENDS ((EV_FEATURES) & 32) +#define EV_FEATURE_OS ((EV_FEATURES) & 64) + +/* these priorities are inclusive, higher priorities will be invoked earlier */ +#ifndef EV_MINPRI +# define EV_MINPRI (EV_FEATURE_CONFIG ? -2 : 0) +#endif +#ifndef EV_MAXPRI +# define EV_MAXPRI (EV_FEATURE_CONFIG ? +2 : 0) +#endif + +#ifndef EV_MULTIPLICITY +# define EV_MULTIPLICITY EV_FEATURE_CONFIG +#endif + +#ifndef EV_PERIODIC_ENABLE +# define EV_PERIODIC_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_STAT_ENABLE +# define EV_STAT_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_PREPARE_ENABLE +# define EV_PREPARE_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_CHECK_ENABLE +# define EV_CHECK_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_IDLE_ENABLE +# define EV_IDLE_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_FORK_ENABLE +# define EV_FORK_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_CLEANUP_ENABLE +# define EV_CLEANUP_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_SIGNAL_ENABLE +# define EV_SIGNAL_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_CHILD_ENABLE +# ifdef _WIN32 +# define EV_CHILD_ENABLE 0 +# else +# define EV_CHILD_ENABLE EV_FEATURE_WATCHERS +#endif +#endif + +#ifndef EV_ASYNC_ENABLE +# define EV_ASYNC_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_EMBED_ENABLE +# define EV_EMBED_ENABLE EV_FEATURE_WATCHERS +#endif + +#ifndef EV_WALK_ENABLE +# define EV_WALK_ENABLE 0 /* not yet */ +#endif + +/*****************************************************************************/ + +#if EV_CHILD_ENABLE && !EV_SIGNAL_ENABLE +# undef EV_SIGNAL_ENABLE +# define EV_SIGNAL_ENABLE 1 +#endif + +/*****************************************************************************/ + +typedef double ev_tstamp; + +#include /* for memmove */ + +#ifndef EV_ATOMIC_T +# include +# define EV_ATOMIC_T sig_atomic_t volatile +#endif + +#if EV_STAT_ENABLE +# ifdef _WIN32 +# include +# include +# endif +# include +#endif + +/* support multiple event loops? */ +#if EV_MULTIPLICITY +struct ev_loop; +# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */ +# define EV_P_ EV_P, /* a loop as first of multiple parameters */ +# define EV_A loop /* a loop as sole argument to a function call */ +# define EV_A_ EV_A, /* a loop as first of multiple arguments */ +# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */ +# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */ +# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */ +# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */ +#else +# define EV_P void +# define EV_P_ +# define EV_A +# define EV_A_ +# define EV_DEFAULT +# define EV_DEFAULT_ +# define EV_DEFAULT_UC +# define EV_DEFAULT_UC_ +# undef EV_EMBED_ENABLE +#endif + +/* EV_INLINE is used for functions in header files */ +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L || __GNUC__ >= 3 +# define EV_INLINE static inline +#else +# define EV_INLINE static +#endif + +#ifdef EV_API_STATIC +# define EV_API_DECL static +#else +# define EV_API_DECL extern +#endif + +/* EV_PROTOTYPES can be used to switch of prototype declarations */ +#ifndef EV_PROTOTYPES +# define EV_PROTOTYPES 1 +#endif + +/*****************************************************************************/ + +#define EV_VERSION_MAJOR 4 +#define EV_VERSION_MINOR 20 + +/* eventmask, revents, events... */ +enum { + EV_UNDEF = (int)0xFFFFFFFF, /* guaranteed to be invalid */ + EV_NONE = 0x00, /* no events */ + EV_READ = 0x01, /* ev_io detected read will not block */ + EV_WRITE = 0x02, /* ev_io detected write will not block */ + EV__IOFDSET = 0x80, /* internal use only */ + EV_IO = EV_READ, /* alias for type-detection */ + EV_TIMER = 0x00000100, /* timer timed out */ +#if EV_COMPAT3 + EV_TIMEOUT = EV_TIMER, /* pre 4.0 API compatibility */ +#endif + EV_PERIODIC = 0x00000200, /* periodic timer timed out */ + EV_SIGNAL = 0x00000400, /* signal was received */ + EV_CHILD = 0x00000800, /* child/pid had status change */ + EV_STAT = 0x00001000, /* stat data changed */ + EV_IDLE = 0x00002000, /* event loop is idling */ + EV_PREPARE = 0x00004000, /* event loop about to poll */ + EV_CHECK = 0x00008000, /* event loop finished poll */ + EV_EMBED = 0x00010000, /* embedded event loop needs sweep */ + EV_FORK = 0x00020000, /* event loop resumed in child */ + EV_CLEANUP = 0x00040000, /* event loop resumed in child */ + EV_ASYNC = 0x00080000, /* async intra-loop signal */ + EV_CUSTOM = 0x01000000, /* for use by user code */ + EV_ERROR = (int)0x80000000 /* sent when an error occurs */ +}; + +/* can be used to add custom fields to all watchers, while losing binary compatibility */ +#ifndef EV_COMMON +# define EV_COMMON void *data; +#endif + +#ifndef EV_CB_DECLARE +# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents); +#endif +#ifndef EV_CB_INVOKE +# define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents)) +#endif + +/* not official, do not use */ +#define EV_CB(type,name) void name (EV_P_ struct ev_ ## type *w, int revents) + +/* + * struct member types: + * private: you may look at them, but not change them, + * and they might not mean anything to you. + * ro: can be read anytime, but only changed when the watcher isn't active. + * rw: can be read and modified anytime, even when the watcher is active. + * + * some internal details that might be helpful for debugging: + * + * active is either 0, which means the watcher is not active, + * or the array index of the watcher (periodics, timers) + * or the array index + 1 (most other watchers) + * or simply 1 for watchers that aren't in some array. + * pending is either 0, in which case the watcher isn't, + * or the array index + 1 in the pendings array. + */ + +#if EV_MINPRI == EV_MAXPRI +# define EV_DECL_PRIORITY +#elif !defined (EV_DECL_PRIORITY) +# define EV_DECL_PRIORITY int priority; +#endif + +/* shared by all watchers */ +#define EV_WATCHER(type) \ + int active; /* private */ \ + int pending; /* private */ \ + EV_DECL_PRIORITY /* private */ \ + EV_COMMON /* rw */ \ + EV_CB_DECLARE (type) /* private */ + +#define EV_WATCHER_LIST(type) \ + EV_WATCHER (type) \ + struct ev_watcher_list *next; /* private */ + +#define EV_WATCHER_TIME(type) \ + EV_WATCHER (type) \ + ev_tstamp at; /* private */ + +/* base class, nothing to see here unless you subclass */ +typedef struct ev_watcher +{ + EV_WATCHER (ev_watcher) +} ev_watcher; + +/* base class, nothing to see here unless you subclass */ +typedef struct ev_watcher_list +{ + EV_WATCHER_LIST (ev_watcher_list) +} ev_watcher_list; + +/* base class, nothing to see here unless you subclass */ +typedef struct ev_watcher_time +{ + EV_WATCHER_TIME (ev_watcher_time) +} ev_watcher_time; + +/* invoked when fd is either EV_READable or EV_WRITEable */ +/* revent EV_READ, EV_WRITE */ +typedef struct ev_io +{ + EV_WATCHER_LIST (ev_io) + + int fd; /* ro */ + int events; /* ro */ +} ev_io; + +/* invoked after a specific time, repeatable (based on monotonic clock) */ +/* revent EV_TIMEOUT */ +typedef struct ev_timer +{ + EV_WATCHER_TIME (ev_timer) + + ev_tstamp repeat; /* rw */ +} ev_timer; + +/* invoked at some specific time, possibly repeating at regular intervals (based on UTC) */ +/* revent EV_PERIODIC */ +typedef struct ev_periodic +{ + EV_WATCHER_TIME (ev_periodic) + + ev_tstamp offset; /* rw */ + ev_tstamp interval; /* rw */ + ev_tstamp (*reschedule_cb)(struct ev_periodic *w, ev_tstamp now) EV_THROW; /* rw */ +} ev_periodic; + +/* invoked when the given signal has been received */ +/* revent EV_SIGNAL */ +typedef struct ev_signal +{ + EV_WATCHER_LIST (ev_signal) + + int signum; /* ro */ +} ev_signal; + +/* invoked when sigchld is received and waitpid indicates the given pid */ +/* revent EV_CHILD */ +/* does not support priorities */ +typedef struct ev_child +{ + EV_WATCHER_LIST (ev_child) + + int flags; /* private */ + int pid; /* ro */ + int rpid; /* rw, holds the received pid */ + int rstatus; /* rw, holds the exit status, use the macros from sys/wait.h */ +} ev_child; + +#if EV_STAT_ENABLE +/* st_nlink = 0 means missing file or other error */ +# ifdef _WIN32 +typedef struct _stati64 ev_statdata; +# else +typedef struct stat ev_statdata; +# endif + +/* invoked each time the stat data changes for a given path */ +/* revent EV_STAT */ +typedef struct ev_stat +{ + EV_WATCHER_LIST (ev_stat) + + ev_timer timer; /* private */ + ev_tstamp interval; /* ro */ + const char *path; /* ro */ + ev_statdata prev; /* ro */ + ev_statdata attr; /* ro */ + + int wd; /* wd for inotify, fd for kqueue */ +} ev_stat; +#endif + +#if EV_IDLE_ENABLE +/* invoked when the nothing else needs to be done, keeps the process from blocking */ +/* revent EV_IDLE */ +typedef struct ev_idle +{ + EV_WATCHER (ev_idle) +} ev_idle; +#endif + +/* invoked for each run of the mainloop, just before the blocking call */ +/* you can still change events in any way you like */ +/* revent EV_PREPARE */ +typedef struct ev_prepare +{ + EV_WATCHER (ev_prepare) +} ev_prepare; + +/* invoked for each run of the mainloop, just after the blocking call */ +/* revent EV_CHECK */ +typedef struct ev_check +{ + EV_WATCHER (ev_check) +} ev_check; + +#if EV_FORK_ENABLE +/* the callback gets invoked before check in the child process when a fork was detected */ +/* revent EV_FORK */ +typedef struct ev_fork +{ + EV_WATCHER (ev_fork) +} ev_fork; +#endif + +#if EV_CLEANUP_ENABLE +/* is invoked just before the loop gets destroyed */ +/* revent EV_CLEANUP */ +typedef struct ev_cleanup +{ + EV_WATCHER (ev_cleanup) +} ev_cleanup; +#endif + +#if EV_EMBED_ENABLE +/* used to embed an event loop inside another */ +/* the callback gets invoked when the event loop has handled events, and can be 0 */ +typedef struct ev_embed +{ + EV_WATCHER (ev_embed) + + struct ev_loop *other; /* ro */ + ev_io io; /* private */ + ev_prepare prepare; /* private */ + ev_check check; /* unused */ + ev_timer timer; /* unused */ + ev_periodic periodic; /* unused */ + ev_idle idle; /* unused */ + ev_fork fork; /* private */ +#if EV_CLEANUP_ENABLE + ev_cleanup cleanup; /* unused */ +#endif +} ev_embed; +#endif + +#if EV_ASYNC_ENABLE +/* invoked when somebody calls ev_async_send on the watcher */ +/* revent EV_ASYNC */ +typedef struct ev_async +{ + EV_WATCHER (ev_async) + + EV_ATOMIC_T sent; /* private */ +} ev_async; + +# define ev_async_pending(w) (+(w)->sent) +#endif + +/* the presence of this union forces similar struct layout */ +union ev_any_watcher +{ + struct ev_watcher w; + struct ev_watcher_list wl; + + struct ev_io io; + struct ev_timer timer; + struct ev_periodic periodic; + struct ev_signal signal; + struct ev_child child; +#if EV_STAT_ENABLE + struct ev_stat stat; +#endif +#if EV_IDLE_ENABLE + struct ev_idle idle; +#endif + struct ev_prepare prepare; + struct ev_check check; +#if EV_FORK_ENABLE + struct ev_fork fork; +#endif +#if EV_CLEANUP_ENABLE + struct ev_cleanup cleanup; +#endif +#if EV_EMBED_ENABLE + struct ev_embed embed; +#endif +#if EV_ASYNC_ENABLE + struct ev_async async; +#endif +}; + +/* flag bits for ev_default_loop and ev_loop_new */ +enum { + /* the default */ + EVFLAG_AUTO = 0x00000000U, /* not quite a mask */ + /* flag bits */ + EVFLAG_NOENV = 0x01000000U, /* do NOT consult environment */ + EVFLAG_FORKCHECK = 0x02000000U, /* check for a fork in each iteration */ + /* debugging/feature disable */ + EVFLAG_NOINOTIFY = 0x00100000U, /* do not attempt to use inotify */ +#if EV_COMPAT3 + EVFLAG_NOSIGFD = 0, /* compatibility to pre-3.9 */ +#endif + EVFLAG_SIGNALFD = 0x00200000U, /* attempt to use signalfd */ + EVFLAG_NOSIGMASK = 0x00400000U /* avoid modifying the signal mask */ +}; + +/* method bits to be ored together */ +enum { + EVBACKEND_SELECT = 0x00000001U, /* about anywhere */ + EVBACKEND_POLL = 0x00000002U, /* !win */ + EVBACKEND_EPOLL = 0x00000004U, /* linux */ + EVBACKEND_KQUEUE = 0x00000008U, /* bsd */ + EVBACKEND_DEVPOLL = 0x00000010U, /* solaris 8 */ /* NYI */ + EVBACKEND_PORT = 0x00000020U, /* solaris 10 */ + EVBACKEND_ALL = 0x0000003FU, /* all known backends */ + EVBACKEND_MASK = 0x0000FFFFU /* all future backends */ +}; + +#if EV_PROTOTYPES +EV_API_DECL int ev_version_major (void) EV_THROW; +EV_API_DECL int ev_version_minor (void) EV_THROW; + +EV_API_DECL unsigned int ev_supported_backends (void) EV_THROW; +EV_API_DECL unsigned int ev_recommended_backends (void) EV_THROW; +EV_API_DECL unsigned int ev_embeddable_backends (void) EV_THROW; + +EV_API_DECL ev_tstamp ev_time (void) EV_THROW; +EV_API_DECL void ev_sleep (ev_tstamp delay) EV_THROW; /* sleep for a while */ + +/* Sets the allocation function to use, works like realloc. + * It is used to allocate and free memory. + * If it returns zero when memory needs to be allocated, the library might abort + * or take some potentially destructive action. + * The default is your system realloc function. + */ +EV_API_DECL void ev_set_allocator (void *(*cb)(void *ptr, long size) EV_THROW) EV_THROW; + +/* set the callback function to call on a + * retryable syscall error + * (such as failed select, poll, epoll_wait) + */ +EV_API_DECL void ev_set_syserr_cb (void (*cb)(const char *msg) EV_THROW) EV_THROW; + +#if EV_MULTIPLICITY + +/* the default loop is the only one that handles signals and child watchers */ +/* you can call this as often as you like */ +EV_API_DECL struct ev_loop *ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; + +#ifdef EV_API_STATIC +EV_API_DECL struct ev_loop *ev_default_loop_ptr; +#endif + +EV_INLINE struct ev_loop * +ev_default_loop_uc_ (void) EV_THROW +{ + extern struct ev_loop *ev_default_loop_ptr; + + return ev_default_loop_ptr; +} + +EV_INLINE int +ev_is_default_loop (EV_P) EV_THROW +{ + return EV_A == EV_DEFAULT_UC; +} + +/* create and destroy alternative loops that don't handle signals */ +EV_API_DECL struct ev_loop *ev_loop_new (unsigned int flags EV_CPP (= 0)) EV_THROW; + +EV_API_DECL ev_tstamp ev_now (EV_P) EV_THROW; /* time w.r.t. timers and the eventloop, updated after each poll */ + +#else + +EV_API_DECL int ev_default_loop (unsigned int flags EV_CPP (= 0)) EV_THROW; /* returns true when successful */ + +EV_API_DECL ev_tstamp ev_rt_now; + +EV_INLINE ev_tstamp +ev_now (void) EV_THROW +{ + return ev_rt_now; +} + +/* looks weird, but ev_is_default_loop (EV_A) still works if this exists */ +EV_INLINE int +ev_is_default_loop (void) EV_THROW +{ + return 1; +} + +#endif /* multiplicity */ + +/* destroy event loops, also works for the default loop */ +EV_API_DECL void ev_loop_destroy (EV_P); + +/* this needs to be called after fork, to duplicate the loop */ +/* when you want to re-use it in the child */ +/* you can call it in either the parent or the child */ +/* you can actually call it at any time, anywhere :) */ +EV_API_DECL void ev_loop_fork (EV_P) EV_THROW; + +EV_API_DECL unsigned int ev_backend (EV_P) EV_THROW; /* backend in use by loop */ + +EV_API_DECL void ev_now_update (EV_P) EV_THROW; /* update event loop time */ + +#if EV_WALK_ENABLE +/* walk (almost) all watchers in the loop of a given type, invoking the */ +/* callback on every such watcher. The callback might stop the watcher, */ +/* but do nothing else with the loop */ +EV_API_DECL void ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_THROW; +#endif + +#endif /* prototypes */ + +/* ev_run flags values */ +enum { + EVRUN_NOWAIT = 1, /* do not block/wait */ + EVRUN_ONCE = 2 /* block *once* only */ +}; + +/* ev_break how values */ +enum { + EVBREAK_CANCEL = 0, /* undo unloop */ + EVBREAK_ONE = 1, /* unloop once */ + EVBREAK_ALL = 2 /* unloop all loops */ +}; + +#if EV_PROTOTYPES +EV_API_DECL int ev_run (EV_P_ int flags EV_CPP (= 0)); +EV_API_DECL void ev_break (EV_P_ int how EV_CPP (= EVBREAK_ONE)) EV_THROW; /* break out of the loop */ + +/* + * ref/unref can be used to add or remove a refcount on the mainloop. every watcher + * keeps one reference. if you have a long-running watcher you never unregister that + * should not keep ev_loop from running, unref() after starting, and ref() before stopping. + */ +EV_API_DECL void ev_ref (EV_P) EV_THROW; +EV_API_DECL void ev_unref (EV_P) EV_THROW; + +/* + * convenience function, wait for a single event, without registering an event watcher + * if timeout is < 0, do wait indefinitely + */ +EV_API_DECL void ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, void *arg), void *arg) EV_THROW; + +# if EV_FEATURE_API +EV_API_DECL unsigned int ev_iteration (EV_P) EV_THROW; /* number of loop iterations */ +EV_API_DECL unsigned int ev_depth (EV_P) EV_THROW; /* #ev_loop enters - #ev_loop leaves */ +EV_API_DECL void ev_verify (EV_P) EV_THROW; /* abort if loop data corrupted */ + +EV_API_DECL void ev_set_io_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */ +EV_API_DECL void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval) EV_THROW; /* sleep at least this time, default 0 */ + +/* advanced stuff for threading etc. support, see docs */ +EV_API_DECL void ev_set_userdata (EV_P_ void *data) EV_THROW; +EV_API_DECL void *ev_userdata (EV_P) EV_THROW; +typedef void (*ev_loop_callback)(EV_P); +EV_API_DECL void ev_set_invoke_pending_cb (EV_P_ ev_loop_callback invoke_pending_cb) EV_THROW; +/* C++ doesn't allow the use of the ev_loop_callback typedef here, so we need to spell it out */ +EV_API_DECL void ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_THROW, void (*acquire)(EV_P) EV_THROW) EV_THROW; + +EV_API_DECL unsigned int ev_pending_count (EV_P) EV_THROW; /* number of pending events, if any */ +EV_API_DECL void ev_invoke_pending (EV_P); /* invoke all pending watchers */ + +/* + * stop/start the timer handling. + */ +EV_API_DECL void ev_suspend (EV_P) EV_THROW; +EV_API_DECL void ev_resume (EV_P) EV_THROW; +#endif + +#endif + +/* these may evaluate ev multiple times, and the other arguments at most once */ +/* either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher */ +#define ev_init(ev,cb_) do { \ + ((ev_watcher *)(void *)(ev))->active = \ + ((ev_watcher *)(void *)(ev))->pending = 0; \ + ev_set_priority ((ev), 0); \ + ev_set_cb ((ev), cb_); \ +} while (0) + +#define ev_io_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0) +#define ev_timer_set(ev,after_,repeat_) do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0) +#define ev_periodic_set(ev,ofs_,ival_,rcb_) do { (ev)->offset = (ofs_); (ev)->interval = (ival_); (ev)->reschedule_cb = (rcb_); } while (0) +#define ev_signal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0) +#define ev_child_set(ev,pid_,trace_) do { (ev)->pid = (pid_); (ev)->flags = !!(trace_); } while (0) +#define ev_stat_set(ev,path_,interval_) do { (ev)->path = (path_); (ev)->interval = (interval_); (ev)->wd = -2; } while (0) +#define ev_idle_set(ev) /* nop, yes, this is a serious in-joke */ +#define ev_prepare_set(ev) /* nop, yes, this is a serious in-joke */ +#define ev_check_set(ev) /* nop, yes, this is a serious in-joke */ +#define ev_embed_set(ev,other_) do { (ev)->other = (other_); } while (0) +#define ev_fork_set(ev) /* nop, yes, this is a serious in-joke */ +#define ev_cleanup_set(ev) /* nop, yes, this is a serious in-joke */ +#define ev_async_set(ev) /* nop, yes, this is a serious in-joke */ + +#define ev_io_init(ev,cb,fd,events) do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0) +#define ev_timer_init(ev,cb,after,repeat) do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0) +#define ev_periodic_init(ev,cb,ofs,ival,rcb) do { ev_init ((ev), (cb)); ev_periodic_set ((ev),(ofs),(ival),(rcb)); } while (0) +#define ev_signal_init(ev,cb,signum) do { ev_init ((ev), (cb)); ev_signal_set ((ev), (signum)); } while (0) +#define ev_child_init(ev,cb,pid,trace) do { ev_init ((ev), (cb)); ev_child_set ((ev),(pid),(trace)); } while (0) +#define ev_stat_init(ev,cb,path,interval) do { ev_init ((ev), (cb)); ev_stat_set ((ev),(path),(interval)); } while (0) +#define ev_idle_init(ev,cb) do { ev_init ((ev), (cb)); ev_idle_set ((ev)); } while (0) +#define ev_prepare_init(ev,cb) do { ev_init ((ev), (cb)); ev_prepare_set ((ev)); } while (0) +#define ev_check_init(ev,cb) do { ev_init ((ev), (cb)); ev_check_set ((ev)); } while (0) +#define ev_embed_init(ev,cb,other) do { ev_init ((ev), (cb)); ev_embed_set ((ev),(other)); } while (0) +#define ev_fork_init(ev,cb) do { ev_init ((ev), (cb)); ev_fork_set ((ev)); } while (0) +#define ev_cleanup_init(ev,cb) do { ev_init ((ev), (cb)); ev_cleanup_set ((ev)); } while (0) +#define ev_async_init(ev,cb) do { ev_init ((ev), (cb)); ev_async_set ((ev)); } while (0) + +#define ev_is_pending(ev) (0 + ((ev_watcher *)(void *)(ev))->pending) /* ro, true when watcher is waiting for callback invocation */ +#define ev_is_active(ev) (0 + ((ev_watcher *)(void *)(ev))->active) /* ro, true when the watcher has been started */ + +#define ev_cb_(ev) (ev)->cb /* rw */ +#define ev_cb(ev) (memmove (&ev_cb_ (ev), &((ev_watcher *)(ev))->cb, sizeof (ev_cb_ (ev))), (ev)->cb) + +#if EV_MINPRI == EV_MAXPRI +# define ev_priority(ev) ((ev), EV_MINPRI) +# define ev_set_priority(ev,pri) ((ev), (pri)) +#else +# define ev_priority(ev) (+(((ev_watcher *)(void *)(ev))->priority)) +# define ev_set_priority(ev,pri) ( (ev_watcher *)(void *)(ev))->priority = (pri) +#endif + +#define ev_periodic_at(ev) (+((ev_watcher_time *)(ev))->at) + +#ifndef ev_set_cb +# define ev_set_cb(ev,cb_) (ev_cb_ (ev) = (cb_), memmove (&((ev_watcher *)(ev))->cb, &ev_cb_ (ev), sizeof (ev_cb_ (ev)))) +#endif + +/* stopping (enabling, adding) a watcher does nothing if it is already running */ +/* stopping (disabling, deleting) a watcher does nothing unless it's already running */ +#if EV_PROTOTYPES + +/* feeds an event into a watcher as if the event actually occurred */ +/* accepts any ev_watcher type */ +EV_API_DECL void ev_feed_event (EV_P_ void *w, int revents) EV_THROW; +EV_API_DECL void ev_feed_fd_event (EV_P_ int fd, int revents) EV_THROW; +#if EV_SIGNAL_ENABLE +EV_API_DECL void ev_feed_signal (int signum) EV_THROW; +EV_API_DECL void ev_feed_signal_event (EV_P_ int signum) EV_THROW; +#endif +EV_API_DECL void ev_invoke (EV_P_ void *w, int revents); +EV_API_DECL int ev_clear_pending (EV_P_ void *w) EV_THROW; + +EV_API_DECL void ev_io_start (EV_P_ ev_io *w) EV_THROW; +EV_API_DECL void ev_io_stop (EV_P_ ev_io *w) EV_THROW; + +EV_API_DECL void ev_timer_start (EV_P_ ev_timer *w) EV_THROW; +EV_API_DECL void ev_timer_stop (EV_P_ ev_timer *w) EV_THROW; +/* stops if active and no repeat, restarts if active and repeating, starts if inactive and repeating */ +EV_API_DECL void ev_timer_again (EV_P_ ev_timer *w) EV_THROW; +/* return remaining time */ +EV_API_DECL ev_tstamp ev_timer_remaining (EV_P_ ev_timer *w) EV_THROW; + +#if EV_PERIODIC_ENABLE +EV_API_DECL void ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW; +EV_API_DECL void ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW; +EV_API_DECL void ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW; +#endif + +/* only supported in the default loop */ +#if EV_SIGNAL_ENABLE +EV_API_DECL void ev_signal_start (EV_P_ ev_signal *w) EV_THROW; +EV_API_DECL void ev_signal_stop (EV_P_ ev_signal *w) EV_THROW; +#endif + +/* only supported in the default loop */ +# if EV_CHILD_ENABLE +EV_API_DECL void ev_child_start (EV_P_ ev_child *w) EV_THROW; +EV_API_DECL void ev_child_stop (EV_P_ ev_child *w) EV_THROW; +# endif + +# if EV_STAT_ENABLE +EV_API_DECL void ev_stat_start (EV_P_ ev_stat *w) EV_THROW; +EV_API_DECL void ev_stat_stop (EV_P_ ev_stat *w) EV_THROW; +EV_API_DECL void ev_stat_stat (EV_P_ ev_stat *w) EV_THROW; +# endif + +# if EV_IDLE_ENABLE +EV_API_DECL void ev_idle_start (EV_P_ ev_idle *w) EV_THROW; +EV_API_DECL void ev_idle_stop (EV_P_ ev_idle *w) EV_THROW; +# endif + +#if EV_PREPARE_ENABLE +EV_API_DECL void ev_prepare_start (EV_P_ ev_prepare *w) EV_THROW; +EV_API_DECL void ev_prepare_stop (EV_P_ ev_prepare *w) EV_THROW; +#endif + +#if EV_CHECK_ENABLE +EV_API_DECL void ev_check_start (EV_P_ ev_check *w) EV_THROW; +EV_API_DECL void ev_check_stop (EV_P_ ev_check *w) EV_THROW; +#endif + +# if EV_FORK_ENABLE +EV_API_DECL void ev_fork_start (EV_P_ ev_fork *w) EV_THROW; +EV_API_DECL void ev_fork_stop (EV_P_ ev_fork *w) EV_THROW; +# endif + +# if EV_CLEANUP_ENABLE +EV_API_DECL void ev_cleanup_start (EV_P_ ev_cleanup *w) EV_THROW; +EV_API_DECL void ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_THROW; +# endif + +# if EV_EMBED_ENABLE +/* only supported when loop to be embedded is in fact embeddable */ +EV_API_DECL void ev_embed_start (EV_P_ ev_embed *w) EV_THROW; +EV_API_DECL void ev_embed_stop (EV_P_ ev_embed *w) EV_THROW; +EV_API_DECL void ev_embed_sweep (EV_P_ ev_embed *w) EV_THROW; +# endif + +# if EV_ASYNC_ENABLE +EV_API_DECL void ev_async_start (EV_P_ ev_async *w) EV_THROW; +EV_API_DECL void ev_async_stop (EV_P_ ev_async *w) EV_THROW; +EV_API_DECL void ev_async_send (EV_P_ ev_async *w) EV_THROW; +# endif + +#if EV_COMPAT3 + #define EVLOOP_NONBLOCK EVRUN_NOWAIT + #define EVLOOP_ONESHOT EVRUN_ONCE + #define EVUNLOOP_CANCEL EVBREAK_CANCEL + #define EVUNLOOP_ONE EVBREAK_ONE + #define EVUNLOOP_ALL EVBREAK_ALL + #if EV_PROTOTYPES + EV_INLINE void ev_loop (EV_P_ int flags) { ev_run (EV_A_ flags); } + EV_INLINE void ev_unloop (EV_P_ int how ) { ev_break (EV_A_ how ); } + EV_INLINE void ev_default_destroy (void) { ev_loop_destroy (EV_DEFAULT); } + EV_INLINE void ev_default_fork (void) { ev_loop_fork (EV_DEFAULT); } + #if EV_FEATURE_API + EV_INLINE unsigned int ev_loop_count (EV_P) { return ev_iteration (EV_A); } + EV_INLINE unsigned int ev_loop_depth (EV_P) { return ev_depth (EV_A); } + EV_INLINE void ev_loop_verify (EV_P) { ev_verify (EV_A); } + #endif + #endif +#else + typedef struct ev_loop ev_loop; +#endif + +#endif + +EV_CPP(}) + +#endif + diff --git a/framework/src/audit/src/libev/ev_epoll.c b/framework/src/audit/src/libev/ev_epoll.c new file mode 100644 index 00000000..8a9b20f6 --- /dev/null +++ b/framework/src/audit/src/libev/ev_epoll.c @@ -0,0 +1,279 @@ +/* + * libev epoll fd activity backend + * + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +/* + * general notes about epoll: + * + * a) epoll silently removes fds from the fd set. as nothing tells us + * that an fd has been removed otherwise, we have to continually + * "rearm" fds that we suspect *might* have changed (same + * problem with kqueue, but much less costly there). + * b) the fact that ADD != MOD creates a lot of extra syscalls due to a) + * and seems not to have any advantage. + * c) the inability to handle fork or file descriptors (think dup) + * limits the applicability over poll, so this is not a generic + * poll replacement. + * d) epoll doesn't work the same as select with many file descriptors + * (such as files). while not critical, no other advanced interface + * seems to share this (rather non-unixy) limitation. + * e) epoll claims to be embeddable, but in practise you never get + * a ready event for the epoll fd (broken: <=2.6.26, working: >=2.6.32). + * f) epoll_ctl returning EPERM means the fd is always ready. + * + * lots of "weird code" and complication handling in this file is due + * to these design problems with epoll, as we try very hard to avoid + * epoll_ctl syscalls for common usage patterns and handle the breakage + * ensuing from receiving events for closed and otherwise long gone + * file descriptors. + */ + +#include + +#define EV_EMASK_EPERM 0x80 + +static void +epoll_modify (EV_P_ int fd, int oev, int nev) +{ + struct epoll_event ev; + unsigned char oldmask; + + /* + * we handle EPOLL_CTL_DEL by ignoring it here + * on the assumption that the fd is gone anyways + * if that is wrong, we have to handle the spurious + * event in epoll_poll. + * if the fd is added again, we try to ADD it, and, if that + * fails, we assume it still has the same eventmask. + */ + if (!nev) + return; + + oldmask = anfds [fd].emask; + anfds [fd].emask = nev; + + /* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */ + ev.data.u64 = (uint64_t)(uint32_t)fd + | ((uint64_t)(uint32_t)++anfds [fd].egen << 32); + ev.events = (nev & EV_READ ? EPOLLIN : 0) + | (nev & EV_WRITE ? EPOLLOUT : 0); + + if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev))) + return; + + if (expect_true (errno == ENOENT)) + { + /* if ENOENT then the fd went away, so try to do the right thing */ + if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev)) + return; + } + else if (expect_true (errno == EEXIST)) + { + /* EEXIST means we ignored a previous DEL, but the fd is still active */ + /* if the kernel mask is the same as the new mask, we assume it hasn't changed */ + if (oldmask == nev) + goto dec_egen; + + if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev)) + return; + } + else if (expect_true (errno == EPERM)) + { + /* EPERM means the fd is always ready, but epoll is too snobbish */ + /* to handle it, unlike select or poll. */ + anfds [fd].emask = EV_EMASK_EPERM; + + /* add fd to epoll_eperms, if not already inside */ + if (!(oldmask & EV_EMASK_EPERM)) + { + array_needsize (int, epoll_eperms, epoll_epermmax, epoll_epermcnt + 1, EMPTY2); + epoll_eperms [epoll_epermcnt++] = fd; + } + + return; + } + + fd_kill (EV_A_ fd); + +dec_egen: + /* we didn't successfully call epoll_ctl, so decrement the generation counter again */ + --anfds [fd].egen; +} + +static void +epoll_poll (EV_P_ ev_tstamp timeout) +{ + int i; + int eventcnt; + + if (expect_false (epoll_epermcnt)) + timeout = 0.; + + /* epoll wait times cannot be larger than (LONG_MAX - 999UL) / HZ msecs, which is below */ + /* the default libev max wait time, however. */ + EV_RELEASE_CB; + eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3); + EV_ACQUIRE_CB; + + if (expect_false (eventcnt < 0)) + { + if (errno != EINTR) + ev_syserr ("(libev) epoll_wait"); + + return; + } + + for (i = 0; i < eventcnt; ++i) + { + struct epoll_event *ev = epoll_events + i; + + int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */ + int want = anfds [fd].events; + int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0) + | (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0); + + /* + * check for spurious notification. + * this only finds spurious notifications on egen updates + * other spurious notifications will be found by epoll_ctl, below + * we assume that fd is always in range, as we never shrink the anfds array + */ + if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32))) + { + /* recreate kernel state */ + postfork = 1; + continue; + } + + if (expect_false (got & ~want)) + { + anfds [fd].emask = want; + + /* + * we received an event but are not interested in it, try mod or del + * this often happens because we optimistically do not unregister fds + * when we are no longer interested in them, but also when we get spurious + * notifications for fds from another process. this is partially handled + * above with the gencounter check (== our fd is not the event fd), and + * partially here, when epoll_ctl returns an error (== a child has the fd + * but we closed it). + */ + ev->events = (want & EV_READ ? EPOLLIN : 0) + | (want & EV_WRITE ? EPOLLOUT : 0); + + /* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */ + /* which is fortunately easy to do for us. */ + if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev)) + { + postfork = 1; /* an error occurred, recreate kernel state */ + continue; + } + } + + fd_event (EV_A_ fd, got); + } + + /* if the receive array was full, increase its size */ + if (expect_false (eventcnt == epoll_eventmax)) + { + ev_free (epoll_events); + epoll_eventmax = array_nextsize (sizeof (struct epoll_event), epoll_eventmax, epoll_eventmax + 1); + epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); + } + + /* now synthesize events for all fds where epoll fails, while select works... */ + for (i = epoll_epermcnt; i--; ) + { + int fd = epoll_eperms [i]; + unsigned char events = anfds [fd].events & (EV_READ | EV_WRITE); + + if (anfds [fd].emask & EV_EMASK_EPERM && events) + fd_event (EV_A_ fd, events); + else + { + epoll_eperms [i] = epoll_eperms [--epoll_epermcnt]; + anfds [fd].emask = 0; + } + } +} + +int inline_size +epoll_init (EV_P_ int flags) +{ +#ifdef EPOLL_CLOEXEC + backend_fd = epoll_create1 (EPOLL_CLOEXEC); + + if (backend_fd < 0 && (errno == EINVAL || errno == ENOSYS)) +#endif + backend_fd = epoll_create (256); + + if (backend_fd < 0) + return 0; + + fcntl (backend_fd, F_SETFD, FD_CLOEXEC); + + backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */ + backend_modify = epoll_modify; + backend_poll = epoll_poll; + + epoll_eventmax = 64; /* initial number of events receivable per poll */ + epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax); + + return EVBACKEND_EPOLL; +} + +void inline_size +epoll_destroy (EV_P) +{ + ev_free (epoll_events); + array_free (epoll_eperm, EMPTY); +} + +void inline_size +epoll_fork (EV_P) +{ + close (backend_fd); + + while ((backend_fd = epoll_create (256)) < 0) + ev_syserr ("(libev) epoll_create"); + + fcntl (backend_fd, F_SETFD, FD_CLOEXEC); + + fd_rearm_all (EV_A); +} + diff --git a/framework/src/audit/src/libev/ev_poll.c b/framework/src/audit/src/libev/ev_poll.c new file mode 100644 index 00000000..48323516 --- /dev/null +++ b/framework/src/audit/src/libev/ev_poll.c @@ -0,0 +1,148 @@ +/* + * libev poll fd activity backend + * + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#include + +void inline_size +pollidx_init (int *base, int count) +{ + /* consider using memset (.., -1, ...), which is practically guaranteed + * to work on all systems implementing poll */ + while (count--) + *base++ = -1; +} + +static void +poll_modify (EV_P_ int fd, int oev, int nev) +{ + int idx; + + if (oev == nev) + return; + + array_needsize (int, pollidxs, pollidxmax, fd + 1, pollidx_init); + + idx = pollidxs [fd]; + + if (idx < 0) /* need to allocate a new pollfd */ + { + pollidxs [fd] = idx = pollcnt++; + array_needsize (struct pollfd, polls, pollmax, pollcnt, EMPTY2); + polls [idx].fd = fd; + } + + assert (polls [idx].fd == fd); + + if (nev) + polls [idx].events = + (nev & EV_READ ? POLLIN : 0) + | (nev & EV_WRITE ? POLLOUT : 0); + else /* remove pollfd */ + { + pollidxs [fd] = -1; + + if (expect_true (idx < --pollcnt)) + { + polls [idx] = polls [pollcnt]; + pollidxs [polls [idx].fd] = idx; + } + } +} + +static void +poll_poll (EV_P_ ev_tstamp timeout) +{ + struct pollfd *p; + int res; + + EV_RELEASE_CB; + res = poll (polls, pollcnt, timeout * 1e3); + EV_ACQUIRE_CB; + + if (expect_false (res < 0)) + { + if (errno == EBADF) + fd_ebadf (EV_A); + else if (errno == ENOMEM && !syserr_cb) + fd_enomem (EV_A); + else if (errno != EINTR) + ev_syserr ("(libev) poll"); + } + else + for (p = polls; res; ++p) + { + assert (("libev: poll() returned illegal result, broken BSD kernel?", p < polls + pollcnt)); + + if (expect_false (p->revents)) /* this expect is debatable */ + { + --res; + + if (expect_false (p->revents & POLLNVAL)) + fd_kill (EV_A_ p->fd); + else + fd_event ( + EV_A_ + p->fd, + (p->revents & (POLLOUT | POLLERR | POLLHUP) ? EV_WRITE : 0) + | (p->revents & (POLLIN | POLLERR | POLLHUP) ? EV_READ : 0) + ); + } + } +} + +int inline_size +poll_init (EV_P_ int flags) +{ + backend_mintime = 1e-3; + backend_modify = poll_modify; + backend_poll = poll_poll; + + pollidxs = 0; pollidxmax = 0; + polls = 0; pollmax = 0; pollcnt = 0; + + return EVBACKEND_POLL; +} + +void inline_size +poll_destroy (EV_P) +{ + ev_free (pollidxs); + ev_free (polls); +} + diff --git a/framework/src/audit/src/libev/ev_select.c b/framework/src/audit/src/libev/ev_select.c new file mode 100644 index 00000000..f38d6ca3 --- /dev/null +++ b/framework/src/audit/src/libev/ev_select.c @@ -0,0 +1,314 @@ +/* + * libev select fd activity backend + * + * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#ifndef _WIN32 +/* for unix systems */ +# include +# ifndef __hpux +/* for REAL unix systems */ +# include +# endif +#endif + +#ifndef EV_SELECT_USE_FD_SET +# ifdef NFDBITS +# define EV_SELECT_USE_FD_SET 0 +# else +# define EV_SELECT_USE_FD_SET 1 +# endif +#endif + +#if EV_SELECT_IS_WINSOCKET +# undef EV_SELECT_USE_FD_SET +# define EV_SELECT_USE_FD_SET 1 +# undef NFDBITS +# define NFDBITS 0 +#endif + +#if !EV_SELECT_USE_FD_SET +# define NFDBYTES (NFDBITS / 8) +#endif + +#include + +static void +select_modify (EV_P_ int fd, int oev, int nev) +{ + if (oev == nev) + return; + + { +#if EV_SELECT_USE_FD_SET + + #if EV_SELECT_IS_WINSOCKET + SOCKET handle = anfds [fd].handle; + #else + int handle = fd; + #endif + + assert (("libev: fd >= FD_SETSIZE passed to fd_set-based select backend", fd < FD_SETSIZE)); + + /* FD_SET is broken on windows (it adds the fd to a set twice or more, + * which eventually leads to overflows). Need to call it only on changes. + */ + #if EV_SELECT_IS_WINSOCKET + if ((oev ^ nev) & EV_READ) + #endif + if (nev & EV_READ) + FD_SET (handle, (fd_set *)vec_ri); + else + FD_CLR (handle, (fd_set *)vec_ri); + + #if EV_SELECT_IS_WINSOCKET + if ((oev ^ nev) & EV_WRITE) + #endif + if (nev & EV_WRITE) + FD_SET (handle, (fd_set *)vec_wi); + else + FD_CLR (handle, (fd_set *)vec_wi); + +#else + + int word = fd / NFDBITS; + fd_mask mask = 1UL << (fd % NFDBITS); + + if (expect_false (vec_max <= word)) + { + int new_max = word + 1; + + vec_ri = ev_realloc (vec_ri, new_max * NFDBYTES); + vec_ro = ev_realloc (vec_ro, new_max * NFDBYTES); /* could free/malloc */ + vec_wi = ev_realloc (vec_wi, new_max * NFDBYTES); + vec_wo = ev_realloc (vec_wo, new_max * NFDBYTES); /* could free/malloc */ + #ifdef _WIN32 + vec_eo = ev_realloc (vec_eo, new_max * NFDBYTES); /* could free/malloc */ + #endif + + for (; vec_max < new_max; ++vec_max) + ((fd_mask *)vec_ri) [vec_max] = + ((fd_mask *)vec_wi) [vec_max] = 0; + } + + ((fd_mask *)vec_ri) [word] |= mask; + if (!(nev & EV_READ)) + ((fd_mask *)vec_ri) [word] &= ~mask; + + ((fd_mask *)vec_wi) [word] |= mask; + if (!(nev & EV_WRITE)) + ((fd_mask *)vec_wi) [word] &= ~mask; +#endif + } +} + +static void +select_poll (EV_P_ ev_tstamp timeout) +{ + struct timeval tv; + int res; + int fd_setsize; + + EV_RELEASE_CB; + EV_TV_SET (tv, timeout); + +#if EV_SELECT_USE_FD_SET + fd_setsize = sizeof (fd_set); +#else + fd_setsize = vec_max * NFDBYTES; +#endif + + memcpy (vec_ro, vec_ri, fd_setsize); + memcpy (vec_wo, vec_wi, fd_setsize); + +#ifdef _WIN32 + /* pass in the write set as except set. + * the idea behind this is to work around a windows bug that causes + * errors to be reported as an exception and not by setting + * the writable bit. this is so uncontrollably lame. + */ + memcpy (vec_eo, vec_wi, fd_setsize); + res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, (fd_set *)vec_eo, &tv); +#elif EV_SELECT_USE_FD_SET + fd_setsize = anfdmax < FD_SETSIZE ? anfdmax : FD_SETSIZE; + res = select (fd_setsize, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); +#else + res = select (vec_max * NFDBITS, (fd_set *)vec_ro, (fd_set *)vec_wo, 0, &tv); +#endif + EV_ACQUIRE_CB; + + if (expect_false (res < 0)) + { + #if EV_SELECT_IS_WINSOCKET + errno = WSAGetLastError (); + #endif + #ifdef WSABASEERR + /* on windows, select returns incompatible error codes, fix this */ + if (errno >= WSABASEERR && errno < WSABASEERR + 1000) + if (errno == WSAENOTSOCK) + errno = EBADF; + else + errno -= WSABASEERR; + #endif + + #ifdef _WIN32 + /* select on windows erroneously returns EINVAL when no fd sets have been + * provided (this is documented). what microsoft doesn't tell you that this bug + * exists even when the fd sets _are_ provided, so we have to check for this bug + * here and emulate by sleeping manually. + * we also get EINVAL when the timeout is invalid, but we ignore this case here + * and assume that EINVAL always means: you have to wait manually. + */ + if (errno == EINVAL) + { + if (timeout) + { + unsigned long ms = timeout * 1e3; + Sleep (ms ? ms : 1); + } + + return; + } + #endif + + if (errno == EBADF) + fd_ebadf (EV_A); + else if (errno == ENOMEM && !syserr_cb) + fd_enomem (EV_A); + else if (errno != EINTR) + ev_syserr ("(libev) select"); + + return; + } + +#if EV_SELECT_USE_FD_SET + + { + int fd; + + for (fd = 0; fd < anfdmax; ++fd) + if (anfds [fd].events) + { + int events = 0; + #if EV_SELECT_IS_WINSOCKET + SOCKET handle = anfds [fd].handle; + #else + int handle = fd; + #endif + + if (FD_ISSET (handle, (fd_set *)vec_ro)) events |= EV_READ; + if (FD_ISSET (handle, (fd_set *)vec_wo)) events |= EV_WRITE; + #ifdef _WIN32 + if (FD_ISSET (handle, (fd_set *)vec_eo)) events |= EV_WRITE; + #endif + + if (expect_true (events)) + fd_event (EV_A_ fd, events); + } + } + +#else + + { + int word, bit; + for (word = vec_max; word--; ) + { + fd_mask word_r = ((fd_mask *)vec_ro) [word]; + fd_mask word_w = ((fd_mask *)vec_wo) [word]; + #ifdef _WIN32 + word_w |= ((fd_mask *)vec_eo) [word]; + #endif + + if (word_r || word_w) + for (bit = NFDBITS; bit--; ) + { + fd_mask mask = 1UL << bit; + int events = 0; + + events |= word_r & mask ? EV_READ : 0; + events |= word_w & mask ? EV_WRITE : 0; + + if (expect_true (events)) + fd_event (EV_A_ word * NFDBITS + bit, events); + } + } + } + +#endif +} + +int inline_size +select_init (EV_P_ int flags) +{ + backend_mintime = 1e-6; + backend_modify = select_modify; + backend_poll = select_poll; + +#if EV_SELECT_USE_FD_SET + vec_ri = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri); + vec_ro = ev_malloc (sizeof (fd_set)); + vec_wi = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi); + vec_wo = ev_malloc (sizeof (fd_set)); + #ifdef _WIN32 + vec_eo = ev_malloc (sizeof (fd_set)); + #endif +#else + vec_max = 0; + vec_ri = 0; + vec_ro = 0; + vec_wi = 0; + vec_wo = 0; + #ifdef _WIN32 + vec_eo = 0; + #endif +#endif + + return EVBACKEND_SELECT; +} + +void inline_size +select_destroy (EV_P) +{ + ev_free (vec_ri); + ev_free (vec_ro); + ev_free (vec_wi); + ev_free (vec_wo); + #ifdef _WIN32 + ev_free (vec_eo); + #endif +} + diff --git a/framework/src/audit/src/libev/ev_vars.h b/framework/src/audit/src/libev/ev_vars.h new file mode 100644 index 00000000..04d4db16 --- /dev/null +++ b/framework/src/audit/src/libev/ev_vars.h @@ -0,0 +1,204 @@ +/* + * loop member variable declarations + * + * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#define VARx(type,name) VAR(name, type name) + +VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */ +VARx(ev_tstamp, mn_now) /* monotonic clock "now" */ +VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */ + +/* for reverse feeding of events */ +VARx(W *, rfeeds) +VARx(int, rfeedmax) +VARx(int, rfeedcnt) + +VAR (pendings, ANPENDING *pendings [NUMPRI]) +VAR (pendingmax, int pendingmax [NUMPRI]) +VAR (pendingcnt, int pendingcnt [NUMPRI]) +VARx(int, pendingpri) /* highest priority currently pending */ +VARx(ev_prepare, pending_w) /* dummy pending watcher */ + +VARx(ev_tstamp, io_blocktime) +VARx(ev_tstamp, timeout_blocktime) + +VARx(int, backend) +VARx(int, activecnt) /* total number of active events ("refcount") */ +VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */ + +VARx(int, backend_fd) +VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */ +VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev)) +VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout)) + +VARx(ANFD *, anfds) +VARx(int, anfdmax) + +VAR (evpipe, int evpipe [2]) +VARx(ev_io, pipe_w) +VARx(EV_ATOMIC_T, pipe_write_wanted) +VARx(EV_ATOMIC_T, pipe_write_skipped) + +#if !defined(_WIN32) || EV_GENWRAP +VARx(pid_t, curpid) +#endif + +VARx(char, postfork) /* true if we need to recreate kernel state after fork */ + +#if EV_USE_SELECT || EV_GENWRAP +VARx(void *, vec_ri) +VARx(void *, vec_ro) +VARx(void *, vec_wi) +VARx(void *, vec_wo) +#if defined(_WIN32) || EV_GENWRAP +VARx(void *, vec_eo) +#endif +VARx(int, vec_max) +#endif + +#if EV_USE_POLL || EV_GENWRAP +VARx(struct pollfd *, polls) +VARx(int, pollmax) +VARx(int, pollcnt) +VARx(int *, pollidxs) /* maps fds into structure indices */ +VARx(int, pollidxmax) +#endif + +#if EV_USE_EPOLL || EV_GENWRAP +VARx(struct epoll_event *, epoll_events) +VARx(int, epoll_eventmax) +VARx(int *, epoll_eperms) +VARx(int, epoll_epermcnt) +VARx(int, epoll_epermmax) +#endif + +#if EV_USE_KQUEUE || EV_GENWRAP +VARx(pid_t, kqueue_fd_pid) +VARx(struct kevent *, kqueue_changes) +VARx(int, kqueue_changemax) +VARx(int, kqueue_changecnt) +VARx(struct kevent *, kqueue_events) +VARx(int, kqueue_eventmax) +#endif + +#if EV_USE_PORT || EV_GENWRAP +VARx(struct port_event *, port_events) +VARx(int, port_eventmax) +#endif + +#if EV_USE_IOCP || EV_GENWRAP +VARx(HANDLE, iocp) +#endif + +VARx(int *, fdchanges) +VARx(int, fdchangemax) +VARx(int, fdchangecnt) + +VARx(ANHE *, timers) +VARx(int, timermax) +VARx(int, timercnt) + +#if EV_PERIODIC_ENABLE || EV_GENWRAP +VARx(ANHE *, periodics) +VARx(int, periodicmax) +VARx(int, periodiccnt) +#endif + +#if EV_IDLE_ENABLE || EV_GENWRAP +VAR (idles, ev_idle **idles [NUMPRI]) +VAR (idlemax, int idlemax [NUMPRI]) +VAR (idlecnt, int idlecnt [NUMPRI]) +#endif +VARx(int, idleall) /* total number */ + +VARx(struct ev_prepare **, prepares) +VARx(int, preparemax) +VARx(int, preparecnt) + +VARx(struct ev_check **, checks) +VARx(int, checkmax) +VARx(int, checkcnt) + +#if EV_FORK_ENABLE || EV_GENWRAP +VARx(struct ev_fork **, forks) +VARx(int, forkmax) +VARx(int, forkcnt) +#endif + +#if EV_CLEANUP_ENABLE || EV_GENWRAP +VARx(struct ev_cleanup **, cleanups) +VARx(int, cleanupmax) +VARx(int, cleanupcnt) +#endif + +#if EV_ASYNC_ENABLE || EV_GENWRAP +VARx(EV_ATOMIC_T, async_pending) +VARx(struct ev_async **, asyncs) +VARx(int, asyncmax) +VARx(int, asynccnt) +#endif + +#if EV_USE_INOTIFY || EV_GENWRAP +VARx(int, fs_fd) +VARx(ev_io, fs_w) +VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */ +VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE]) +#endif + +VARx(EV_ATOMIC_T, sig_pending) +#if EV_USE_SIGNALFD || EV_GENWRAP +VARx(int, sigfd) +VARx(ev_io, sigfd_w) +VARx(sigset_t, sigfd_set) +#endif + +VARx(unsigned int, origflags) /* original loop flags */ + +#if EV_FEATURE_API || EV_GENWRAP +VARx(unsigned int, loop_count) /* total number of loop iterations/blocks */ +VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */ + +VARx(void *, userdata) +/* C++ doesn't support the ev_loop_callback typedef here. stinks. */ +VAR (release_cb, void (*release_cb)(EV_P) EV_THROW) +VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_THROW) +VAR (invoke_cb , ev_loop_callback invoke_cb) +#endif + +#undef VARx + diff --git a/framework/src/audit/src/libev/ev_wrap.h b/framework/src/audit/src/libev/ev_wrap.h new file mode 100644 index 00000000..ad989ea7 --- /dev/null +++ b/framework/src/audit/src/libev/ev_wrap.h @@ -0,0 +1,200 @@ +/* DO NOT EDIT, automatically generated by update_ev_wrap */ +#ifndef EV_WRAP_H +#define EV_WRAP_H +#define acquire_cb ((loop)->acquire_cb) +#define activecnt ((loop)->activecnt) +#define anfdmax ((loop)->anfdmax) +#define anfds ((loop)->anfds) +#define async_pending ((loop)->async_pending) +#define asynccnt ((loop)->asynccnt) +#define asyncmax ((loop)->asyncmax) +#define asyncs ((loop)->asyncs) +#define backend ((loop)->backend) +#define backend_fd ((loop)->backend_fd) +#define backend_mintime ((loop)->backend_mintime) +#define backend_modify ((loop)->backend_modify) +#define backend_poll ((loop)->backend_poll) +#define checkcnt ((loop)->checkcnt) +#define checkmax ((loop)->checkmax) +#define checks ((loop)->checks) +#define cleanupcnt ((loop)->cleanupcnt) +#define cleanupmax ((loop)->cleanupmax) +#define cleanups ((loop)->cleanups) +#define curpid ((loop)->curpid) +#define epoll_epermcnt ((loop)->epoll_epermcnt) +#define epoll_epermmax ((loop)->epoll_epermmax) +#define epoll_eperms ((loop)->epoll_eperms) +#define epoll_eventmax ((loop)->epoll_eventmax) +#define epoll_events ((loop)->epoll_events) +#define evpipe ((loop)->evpipe) +#define fdchangecnt ((loop)->fdchangecnt) +#define fdchangemax ((loop)->fdchangemax) +#define fdchanges ((loop)->fdchanges) +#define forkcnt ((loop)->forkcnt) +#define forkmax ((loop)->forkmax) +#define forks ((loop)->forks) +#define fs_2625 ((loop)->fs_2625) +#define fs_fd ((loop)->fs_fd) +#define fs_hash ((loop)->fs_hash) +#define fs_w ((loop)->fs_w) +#define idleall ((loop)->idleall) +#define idlecnt ((loop)->idlecnt) +#define idlemax ((loop)->idlemax) +#define idles ((loop)->idles) +#define invoke_cb ((loop)->invoke_cb) +#define io_blocktime ((loop)->io_blocktime) +#define iocp ((loop)->iocp) +#define kqueue_changecnt ((loop)->kqueue_changecnt) +#define kqueue_changemax ((loop)->kqueue_changemax) +#define kqueue_changes ((loop)->kqueue_changes) +#define kqueue_eventmax ((loop)->kqueue_eventmax) +#define kqueue_events ((loop)->kqueue_events) +#define kqueue_fd_pid ((loop)->kqueue_fd_pid) +#define loop_count ((loop)->loop_count) +#define loop_depth ((loop)->loop_depth) +#define loop_done ((loop)->loop_done) +#define mn_now ((loop)->mn_now) +#define now_floor ((loop)->now_floor) +#define origflags ((loop)->origflags) +#define pending_w ((loop)->pending_w) +#define pendingcnt ((loop)->pendingcnt) +#define pendingmax ((loop)->pendingmax) +#define pendingpri ((loop)->pendingpri) +#define pendings ((loop)->pendings) +#define periodiccnt ((loop)->periodiccnt) +#define periodicmax ((loop)->periodicmax) +#define periodics ((loop)->periodics) +#define pipe_w ((loop)->pipe_w) +#define pipe_write_skipped ((loop)->pipe_write_skipped) +#define pipe_write_wanted ((loop)->pipe_write_wanted) +#define pollcnt ((loop)->pollcnt) +#define pollidxmax ((loop)->pollidxmax) +#define pollidxs ((loop)->pollidxs) +#define pollmax ((loop)->pollmax) +#define polls ((loop)->polls) +#define port_eventmax ((loop)->port_eventmax) +#define port_events ((loop)->port_events) +#define postfork ((loop)->postfork) +#define preparecnt ((loop)->preparecnt) +#define preparemax ((loop)->preparemax) +#define prepares ((loop)->prepares) +#define release_cb ((loop)->release_cb) +#define rfeedcnt ((loop)->rfeedcnt) +#define rfeedmax ((loop)->rfeedmax) +#define rfeeds ((loop)->rfeeds) +#define rtmn_diff ((loop)->rtmn_diff) +#define sig_pending ((loop)->sig_pending) +#define sigfd ((loop)->sigfd) +#define sigfd_set ((loop)->sigfd_set) +#define sigfd_w ((loop)->sigfd_w) +#define timeout_blocktime ((loop)->timeout_blocktime) +#define timercnt ((loop)->timercnt) +#define timermax ((loop)->timermax) +#define timers ((loop)->timers) +#define userdata ((loop)->userdata) +#define vec_eo ((loop)->vec_eo) +#define vec_max ((loop)->vec_max) +#define vec_ri ((loop)->vec_ri) +#define vec_ro ((loop)->vec_ro) +#define vec_wi ((loop)->vec_wi) +#define vec_wo ((loop)->vec_wo) +#else +#undef EV_WRAP_H +#undef acquire_cb +#undef activecnt +#undef anfdmax +#undef anfds +#undef async_pending +#undef asynccnt +#undef asyncmax +#undef asyncs +#undef backend +#undef backend_fd +#undef backend_mintime +#undef backend_modify +#undef backend_poll +#undef checkcnt +#undef checkmax +#undef checks +#undef cleanupcnt +#undef cleanupmax +#undef cleanups +#undef curpid +#undef epoll_epermcnt +#undef epoll_epermmax +#undef epoll_eperms +#undef epoll_eventmax +#undef epoll_events +#undef evpipe +#undef fdchangecnt +#undef fdchangemax +#undef fdchanges +#undef forkcnt +#undef forkmax +#undef forks +#undef fs_2625 +#undef fs_fd +#undef fs_hash +#undef fs_w +#undef idleall +#undef idlecnt +#undef idlemax +#undef idles +#undef invoke_cb +#undef io_blocktime +#undef iocp +#undef kqueue_changecnt +#undef kqueue_changemax +#undef kqueue_changes +#undef kqueue_eventmax +#undef kqueue_events +#undef kqueue_fd_pid +#undef loop_count +#undef loop_depth +#undef loop_done +#undef mn_now +#undef now_floor +#undef origflags +#undef pending_w +#undef pendingcnt +#undef pendingmax +#undef pendingpri +#undef pendings +#undef periodiccnt +#undef periodicmax +#undef periodics +#undef pipe_w +#undef pipe_write_skipped +#undef pipe_write_wanted +#undef pollcnt +#undef pollidxmax +#undef pollidxs +#undef pollmax +#undef polls +#undef port_eventmax +#undef port_events +#undef postfork +#undef preparecnt +#undef preparemax +#undef prepares +#undef release_cb +#undef rfeedcnt +#undef rfeedmax +#undef rfeeds +#undef rtmn_diff +#undef sig_pending +#undef sigfd +#undef sigfd_set +#undef sigfd_w +#undef timeout_blocktime +#undef timercnt +#undef timermax +#undef timers +#undef userdata +#undef vec_eo +#undef vec_max +#undef vec_ri +#undef vec_ro +#undef vec_wi +#undef vec_wo +#endif diff --git a/framework/src/audit/src/libev/event.c b/framework/src/audit/src/libev/event.c new file mode 100644 index 00000000..5586cd35 --- /dev/null +++ b/framework/src/audit/src/libev/event.c @@ -0,0 +1,425 @@ +/* + * libevent compatibility layer + * + * Copyright (c) 2007,2008,2009,2010,2012 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#include +#include +#include + +#ifdef EV_EVENT_H +# include EV_EVENT_H +#else +# include "event.h" +#endif + +#if EV_MULTIPLICITY +# define dLOOPev struct ev_loop *loop = (struct ev_loop *)ev->ev_base +# define dLOOPbase struct ev_loop *loop = (struct ev_loop *)base +#else +# define dLOOPev +# define dLOOPbase +#endif + +/* never accessed, will always be cast from/to ev_loop */ +struct event_base +{ + int dummy; +}; + +static struct event_base *ev_x_cur; + +static ev_tstamp +ev_tv_get (struct timeval *tv) +{ + if (tv) + { + ev_tstamp after = tv->tv_sec + tv->tv_usec * 1e-6; + return after ? after : 1e-6; + } + else + return -1.; +} + +#define EVENT_STRINGIFY(s) # s +#define EVENT_VERSION(a,b) EVENT_STRINGIFY (a) "." EVENT_STRINGIFY (b) + +const char * +event_get_version (void) +{ + /* returns ABI, not API or library, version */ + return EVENT_VERSION (EV_VERSION_MAJOR, EV_VERSION_MINOR); +} + +const char * +event_get_method (void) +{ + return "libev"; +} + +void *event_init (void) +{ +#if EV_MULTIPLICITY + if (ev_x_cur) + ev_x_cur = (struct event_base *)ev_loop_new (EVFLAG_AUTO); + else + ev_x_cur = (struct event_base *)ev_default_loop (EVFLAG_AUTO); +#else + assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY", !ev_x_cur)); + + ev_x_cur = (struct event_base *)(long)ev_default_loop (EVFLAG_AUTO); +#endif + + return ev_x_cur; +} + +const char * +event_base_get_method (const struct event_base *base) +{ + return "libev"; +} + +struct event_base * +event_base_new (void) +{ +#if EV_MULTIPLICITY + return (struct event_base *)ev_loop_new (EVFLAG_AUTO); +#else + assert (("libev: multiple event bases not supported when not compiled with EV_MULTIPLICITY")); + return NULL; +#endif +} + +void event_base_free (struct event_base *base) +{ + dLOOPbase; + +#if EV_MULTIPLICITY + if (!ev_is_default_loop (loop)) + ev_loop_destroy (loop); +#endif +} + +int event_dispatch (void) +{ + return event_base_dispatch (ev_x_cur); +} + +#ifdef EV_STANDALONE +void event_set_log_callback (event_log_cb cb) +{ + /* nop */ +} +#endif + +int event_loop (int flags) +{ + return event_base_loop (ev_x_cur, flags); +} + +int event_loopexit (struct timeval *tv) +{ + return event_base_loopexit (ev_x_cur, tv); +} + +event_callback_fn event_get_callback +(const struct event *ev) +{ + return ev->ev_callback; +} + +static void +ev_x_cb (struct event *ev, int revents) +{ + revents &= EV_READ | EV_WRITE | EV_TIMER | EV_SIGNAL; + + ev->ev_res = revents; + ev->ev_callback (ev->ev_fd, (short)revents, ev->ev_arg); +} + +static void +ev_x_cb_sig (EV_P_ struct ev_signal *w, int revents) +{ + struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.sig)); + + if (revents & EV_ERROR) + event_del (ev); + + ev_x_cb (ev, revents); +} + +static void +ev_x_cb_io (EV_P_ struct ev_io *w, int revents) +{ + struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, iosig.io)); + + if ((revents & EV_ERROR) || !(ev->ev_events & EV_PERSIST)) + event_del (ev); + + ev_x_cb (ev, revents); +} + +static void +ev_x_cb_to (EV_P_ struct ev_timer *w, int revents) +{ + struct event *ev = (struct event *)(((char *)w) - offsetof (struct event, to)); + + event_del (ev); + + ev_x_cb (ev, revents); +} + +void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg) +{ + if (events & EV_SIGNAL) + ev_init (&ev->iosig.sig, ev_x_cb_sig); + else + ev_init (&ev->iosig.io, ev_x_cb_io); + + ev_init (&ev->to, ev_x_cb_to); + + ev->ev_base = ev_x_cur; /* not threadsafe, but it's how libevent works */ + ev->ev_fd = fd; + ev->ev_events = events; + ev->ev_pri = 0; + ev->ev_callback = cb; + ev->ev_arg = arg; + ev->ev_res = 0; + ev->ev_flags = EVLIST_INIT; +} + +int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv) +{ + return event_base_once (ev_x_cur, fd, events, cb, arg, tv); +} + +int event_add (struct event *ev, struct timeval *tv) +{ + dLOOPev; + + if (ev->ev_events & EV_SIGNAL) + { + if (!ev_is_active (&ev->iosig.sig)) + { + ev_signal_set (&ev->iosig.sig, ev->ev_fd); + ev_signal_start (EV_A_ &ev->iosig.sig); + + ev->ev_flags |= EVLIST_SIGNAL; + } + } + else if (ev->ev_events & (EV_READ | EV_WRITE)) + { + if (!ev_is_active (&ev->iosig.io)) + { + ev_io_set (&ev->iosig.io, ev->ev_fd, ev->ev_events & (EV_READ | EV_WRITE)); + ev_io_start (EV_A_ &ev->iosig.io); + + ev->ev_flags |= EVLIST_INSERTED; + } + } + + if (tv) + { + ev->to.repeat = ev_tv_get (tv); + ev_timer_again (EV_A_ &ev->to); + ev->ev_flags |= EVLIST_TIMEOUT; + } + else + { + ev_timer_stop (EV_A_ &ev->to); + ev->ev_flags &= ~EVLIST_TIMEOUT; + } + + ev->ev_flags |= EVLIST_ACTIVE; + + return 0; +} + +int event_del (struct event *ev) +{ + dLOOPev; + + if (ev->ev_events & EV_SIGNAL) + ev_signal_stop (EV_A_ &ev->iosig.sig); + else if (ev->ev_events & (EV_READ | EV_WRITE)) + ev_io_stop (EV_A_ &ev->iosig.io); + + if (ev_is_active (&ev->to)) + ev_timer_stop (EV_A_ &ev->to); + + ev->ev_flags = EVLIST_INIT; + + return 0; +} + +void event_active (struct event *ev, int res, short ncalls) +{ + dLOOPev; + + if (res & EV_TIMEOUT) + ev_feed_event (EV_A_ &ev->to, res & EV_TIMEOUT); + + if (res & EV_SIGNAL) + ev_feed_event (EV_A_ &ev->iosig.sig, res & EV_SIGNAL); + + if (res & (EV_READ | EV_WRITE)) + ev_feed_event (EV_A_ &ev->iosig.io, res & (EV_READ | EV_WRITE)); +} + +int event_pending (struct event *ev, short events, struct timeval *tv) +{ + short revents = 0; + dLOOPev; + + if (ev->ev_events & EV_SIGNAL) + { + /* sig */ + if (ev_is_active (&ev->iosig.sig) || ev_is_pending (&ev->iosig.sig)) + revents |= EV_SIGNAL; + } + else if (ev->ev_events & (EV_READ | EV_WRITE)) + { + /* io */ + if (ev_is_active (&ev->iosig.io) || ev_is_pending (&ev->iosig.io)) + revents |= ev->ev_events & (EV_READ | EV_WRITE); + } + + if (ev->ev_events & EV_TIMEOUT || ev_is_active (&ev->to) || ev_is_pending (&ev->to)) + { + revents |= EV_TIMEOUT; + + if (tv) + { + ev_tstamp at = ev_now (EV_A); + + tv->tv_sec = (long)at; + tv->tv_usec = (long)((at - (ev_tstamp)tv->tv_sec) * 1e6); + } + } + + return events & revents; +} + +int event_priority_init (int npri) +{ + return event_base_priority_init (ev_x_cur, npri); +} + +int event_priority_set (struct event *ev, int pri) +{ + ev->ev_pri = pri; + + return 0; +} + +int event_base_set (struct event_base *base, struct event *ev) +{ + ev->ev_base = base; + + return 0; +} + +int event_base_loop (struct event_base *base, int flags) +{ + dLOOPbase; + + return !ev_run (EV_A_ flags); +} + +int event_base_dispatch (struct event_base *base) +{ + return event_base_loop (base, 0); +} + +static void +ev_x_loopexit_cb (int revents, void *base) +{ + dLOOPbase; + + ev_break (EV_A_ EVBREAK_ONE); +} + +int event_base_loopexit (struct event_base *base, struct timeval *tv) +{ + ev_tstamp after = ev_tv_get (tv); + dLOOPbase; + + ev_once (EV_A_ -1, 0, after >= 0. ? after : 0., ev_x_loopexit_cb, (void *)base); + + return 0; +} + +struct ev_x_once +{ + int fd; + void (*cb)(int, short, void *); + void *arg; +}; + +static void +ev_x_once_cb (int revents, void *arg) +{ + struct ev_x_once *once = (struct ev_x_once *)arg; + + once->cb (once->fd, (short)revents, once->arg); + free (once); +} + +int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv) +{ + struct ev_x_once *once = (struct ev_x_once *)malloc (sizeof (struct ev_x_once)); + dLOOPbase; + + if (!once) + return -1; + + once->fd = fd; + once->cb = cb; + once->arg = arg; + + ev_once (EV_A_ fd, events & (EV_READ | EV_WRITE), ev_tv_get (tv), ev_x_once_cb, (void *)once); + + return 0; +} + +int event_base_priority_init (struct event_base *base, int npri) +{ + /*dLOOPbase;*/ + + return 0; +} + diff --git a/framework/src/audit/src/libev/event.h b/framework/src/audit/src/libev/event.h new file mode 100644 index 00000000..aa81928f --- /dev/null +++ b/framework/src/audit/src/libev/event.h @@ -0,0 +1,177 @@ +/* + * libevent compatibility header, only core events supported + * + * Copyright (c) 2007,2008,2010,2012 Marc Alexander Lehmann + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- + * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- + * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- + * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License ("GPL") version 2 or any later version, + * in which case the provisions of the GPL are applicable instead of + * the above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the BSD license, indicate your decision + * by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file under + * either the BSD or the GPL. + */ + +#ifndef EVENT_H_ +#define EVENT_H_ + +#ifdef EV_H +# include EV_H +#else +# include "ev.h" +#endif + +#ifndef EVLOOP_NONBLOCK +# define EVLOOP_NONBLOCK EVRUN_NOWAIT +#endif +#ifndef EVLOOP_ONESHOT +# define EVLOOP_ONESHOT EVRUN_ONCE +#endif +#ifndef EV_TIMEOUT +# define EV_TIMEOUT EV_TIMER +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* we need sys/time.h for struct timeval only */ +#if !defined (WIN32) || defined (__MINGW32__) +# include /* mingw seems to need this, for whatever reason */ +# include +#endif + +struct event_base; + +#define EVLIST_TIMEOUT 0x01 +#define EVLIST_INSERTED 0x02 +#define EVLIST_SIGNAL 0x04 +#define EVLIST_ACTIVE 0x08 +#define EVLIST_INTERNAL 0x10 +#define EVLIST_INIT 0x80 + +typedef void (*event_callback_fn)(int, short, void *); + +struct event +{ + /* libev watchers we map onto */ + union { + struct ev_io io; + struct ev_signal sig; + } iosig; + struct ev_timer to; + + /* compatibility slots */ + struct event_base *ev_base; + event_callback_fn ev_callback; + void *ev_arg; + int ev_fd; + int ev_pri; + int ev_res; + int ev_flags; + short ev_events; +}; + +event_callback_fn event_get_callback (const struct event *ev); + +#define EV_READ EV_READ +#define EV_WRITE EV_WRITE +#define EV_PERSIST 0x10 +#define EV_ET 0x20 /* nop */ + +#define EVENT_SIGNAL(ev) ((int) (ev)->ev_fd) +#define EVENT_FD(ev) ((int) (ev)->ev_fd) + +#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT) + +#define evtimer_add(ev,tv) event_add (ev, tv) +#define evtimer_set(ev,cb,data) event_set (ev, -1, 0, cb, data) +#define evtimer_del(ev) event_del (ev) +#define evtimer_pending(ev,tv) event_pending (ev, EV_TIMEOUT, tv) +#define evtimer_initialized(ev) event_initialized (ev) + +#define timeout_add(ev,tv) evtimer_add (ev, tv) +#define timeout_set(ev,cb,data) evtimer_set (ev, cb, data) +#define timeout_del(ev) evtimer_del (ev) +#define timeout_pending(ev,tv) evtimer_pending (ev, tv) +#define timeout_initialized(ev) evtimer_initialized (ev) + +#define signal_add(ev,tv) event_add (ev, tv) +#define signal_set(ev,sig,cb,data) event_set (ev, sig, EV_SIGNAL | EV_PERSIST, cb, data) +#define signal_del(ev) event_del (ev) +#define signal_pending(ev,tv) event_pending (ev, EV_SIGNAL, tv) +#define signal_initialized(ev) event_initialized (ev) + +const char *event_get_version (void); +const char *event_get_method (void); + +void *event_init (void); +void event_base_free (struct event_base *base); + +#define EVLOOP_ONCE EVLOOP_ONESHOT +int event_loop (int); +int event_loopexit (struct timeval *tv); +int event_dispatch (void); + +#define _EVENT_LOG_DEBUG 0 +#define _EVENT_LOG_MSG 1 +#define _EVENT_LOG_WARN 2 +#define _EVENT_LOG_ERR 3 +typedef void (*event_log_cb)(int severity, const char *msg); +void event_set_log_callback(event_log_cb cb); + +void event_set (struct event *ev, int fd, short events, void (*cb)(int, short, void *), void *arg); +int event_once (int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv); + +int event_add (struct event *ev, struct timeval *tv); +int event_del (struct event *ev); +void event_active (struct event *ev, int res, short ncalls); /* ncalls is being ignored */ + +int event_pending (struct event *ev, short, struct timeval *tv); + +int event_priority_init (int npri); +int event_priority_set (struct event *ev, int pri); + +struct event_base *event_base_new (void); +const char *event_base_get_method (const struct event_base *); +int event_base_set (struct event_base *base, struct event *ev); +int event_base_loop (struct event_base *base, int); +int event_base_loopexit (struct event_base *base, struct timeval *tv); +int event_base_dispatch (struct event_base *base); +int event_base_once (struct event_base *base, int fd, short events, void (*cb)(int, short, void *), void *arg, struct timeval *tv); +int event_base_priority_init (struct event_base *base, int fd); + +/* next line is different in the libevent+libev version */ +/*libevent-include*/ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/framework/src/audit/src/libev/libev.m4 b/framework/src/audit/src/libev/libev.m4 new file mode 100644 index 00000000..439fbde2 --- /dev/null +++ b/framework/src/audit/src/libev/libev.m4 @@ -0,0 +1,42 @@ +dnl this file is part of libev, do not make local modifications +dnl http://software.schmorp.de/pkg/libev + +dnl libev support +AC_CHECK_HEADERS(sys/inotify.h sys/epoll.h sys/event.h port.h poll.h sys/select.h sys/eventfd.h sys/signalfd.h) + +AC_CHECK_FUNCS(inotify_init epoll_ctl kqueue port_create poll select eventfd signalfd) + +AC_CHECK_FUNCS(clock_gettime, [], [ + dnl on linux, try syscall wrapper first + if test $(uname) = Linux; then + AC_MSG_CHECKING(for clock_gettime syscall) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [#include + #include + #include ], + [struct timespec ts; int status = syscall (SYS_clock_gettime, CLOCK_REALTIME, &ts)])], + [ac_have_clock_syscall=1 + AC_DEFINE(HAVE_CLOCK_SYSCALL, 1, Define to 1 to use the syscall interface for clock_gettime) + AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no)]) + fi + if test -z "$LIBEV_M4_AVOID_LIBRT" && test -z "$ac_have_clock_syscall"; then + AC_CHECK_LIB(rt, clock_gettime) + unset ac_cv_func_clock_gettime + AC_CHECK_FUNCS(clock_gettime) + fi +]) + +AC_CHECK_FUNCS(nanosleep, [], [ + if test -z "$LIBEV_M4_AVOID_LIBRT"; then + AC_CHECK_LIB(rt, nanosleep) + unset ac_cv_func_nanosleep + AC_CHECK_FUNCS(nanosleep) + fi +]) + +if test -z "$LIBEV_M4_AVOID_LIBM"; then + LIBM=m +fi +AC_SEARCH_LIBS(floor, $LIBM, [AC_DEFINE(HAVE_FLOOR, 1, Define to 1 if the floor function is available)]) + diff --git a/framework/src/audit/src/mt/Makefile.am b/framework/src/audit/src/mt/Makefile.am new file mode 100644 index 00000000..1d613d79 --- /dev/null +++ b/framework/src/audit/src/mt/Makefile.am @@ -0,0 +1,49 @@ +# Makefile.am -- +# Copyright 2005-06,2008,2012,2014-15 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# +# This make file builds a static multi-threaded libary for the audit +# daemon's own use. + +AUTOMAKE_OPTIONS = no-dependencies +AM_CPPFLAGS = -I${top_srcdir}/lib -I${top_builddir}/lib +CONFIG_CLEAN_FILES = *.rej *.orig +AM_CFLAGS = -fPIC -DPIC -D_REENTRANT -D_GNU_SOURCE -DNO_TABLES + +noinst_LIBRARIES = libauditmt.a + +libauditmt_a_SOURCES = ${top_srcdir}/lib/libaudit.c \ + ${top_srcdir}/lib/message.c ${top_srcdir}/lib/netlink.c \ + ${top_srcdir}/lib/lookup_table.c ${top_srcdir}/lib/audit_logging.c \ + ${top_srcdir}/lib/deprecated.c ${top_srcdir}/lib/strsplit.c +libauditmt_a_HEADERS: ${top_builddir}/config.h ${top_srcdir}/lib/libaudit.h \ + ${top_srcdir}/lib/private.h +libauditmt_a_DEPENDENCIES = $(libaudit_a_SOURCES) ${top_builddir}/config.h \ + ${top_srcdir}/lib/gen_tables.h ${top_builddir}/lib/i386_tables.h \ + ${top_builddir}/lib/ia64_tables.h ${top_builddir}/lib/ppc_tables.h \ + ${top_builddir}/lib/s390_tables.h ${top_builddir}/lib/s390x_tables.h \ + ${top_builddir}/lib/x86_64_tables.h ${top_srcdir}/lib/private.h \ + ${top_builddir}/lib/actiontabs.h ${top_builddir}/lib/fieldtabs.h \ + ${top_builddir}/lib/flagtabs.h ${top_builddir}/lib/ftypetabs.h \ + ${top_builddir}/lib/machinetabs.h ${top_builddir}/lib/msg_typetabs.h \ + ${top_builddir}/lib/optabs.h +AM_LDFLAGS = -Wl,-z,relro + +lib_OBJECTS = $(libauditmt_a_OBJECTS) diff --git a/framework/src/audit/src/test/Makefile.am b/framework/src/audit/src/test/Makefile.am new file mode 100644 index 00000000..b6f44edb --- /dev/null +++ b/framework/src/audit/src/test/Makefile.am @@ -0,0 +1,26 @@ +# Copyright 2008,2014,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib -I${top_srcdir}/src +check_PROGRAMS = ilist_test slist_test +TESTS = $(check_PROGRAMS) +ilist_test_LDADD = ${top_builddir}/src/ausearch-int.o +slist_test_LDADD = ${top_builddir}/src/ausearch-string.o diff --git a/framework/src/audit/src/test/ilist_test.c b/framework/src/audit/src/test/ilist_test.c new file mode 100644 index 00000000..85787126 --- /dev/null +++ b/framework/src/audit/src/test/ilist_test.c @@ -0,0 +1,69 @@ +#include +#include "ausearch-int.h" + +int main(void) +{ + int i = 0; + ilist e; + int_node *node; + + ilist_create(&e); + + // This first test checks to see if list is + // created in a numeric order + ilist_add_if_uniq(&e, 6, 0); + ilist_add_if_uniq(&e, 5, 0); + ilist_add_if_uniq(&e, 7, 0); + ilist_add_if_uniq(&e, 1, 0); + ilist_add_if_uniq(&e, 8, 0); + ilist_add_if_uniq(&e, 2, 0); + ilist_add_if_uniq(&e, 9, 0); + ilist_add_if_uniq(&e, 0, 0); + ilist_add_if_uniq(&e, 4, 0); + ilist_add_if_uniq(&e, 3, 0); + + ilist_first(&e); + do { + node = ilist_get_cur(&e); + if (i != node->num) { + printf("Test failed - i:%d != num:%d\n", i, node->num); + return 1; + } + i++; + } while ((node = ilist_next(&e))); + + ilist_clear(&e); + puts("starting sort test"); + + // Now test to see if the sort function works + // Fill the list exactly backwards + ilist_add_if_uniq(&e, 3, 0); + ilist_add_if_uniq(&e, 3, 0); + ilist_add_if_uniq(&e, 4, 0); + ilist_add_if_uniq(&e, 3, 0); + ilist_add_if_uniq(&e, 4, 0); + ilist_add_if_uniq(&e, 2, 0); + ilist_add_if_uniq(&e, 4, 0); + ilist_add_if_uniq(&e, 2, 0); + ilist_add_if_uniq(&e, 4, 0); + ilist_add_if_uniq(&e, 1, 0); + + ilist_sort_by_hits(&e); + + i = 0; + ilist_first(&e); + do { + node = ilist_get_cur(&e); + if (node->hits != (4-i)) { + printf("Sort test failed - i:%d != ihits:%d\n", i, node->hits); + return 1; + } + i++; + } while ((node = ilist_next(&e))); + + ilist_clear(&e); + + printf("ilist tests passed\n"); + return 0; +} + diff --git a/framework/src/audit/src/test/slist_test.c b/framework/src/audit/src/test/slist_test.c new file mode 100644 index 00000000..e0336149 --- /dev/null +++ b/framework/src/audit/src/test/slist_test.c @@ -0,0 +1,98 @@ +#include +#include +#include "ausearch-string.h" + +slist s; + +int print_list(void) +{ + int cnt = 0; + slist_first(&s); + do { + snode *cur = slist_get_cur(&s); + if (cur) { + cnt++; + printf("%s\n", cur->str); + } + } while (slist_next(&s)); + return cnt; +} + +int main(void) +{ + snode n, *node; + int rc, i = 0; + + slist_create(&s); + + // This first test checks to see if list is + // created in a numeric order + slist_add_if_uniq(&s, "test1"); + slist_add_if_uniq(&s, "test2"); + slist_first(&s); + slist_add_if_uniq(&s, "test3"); + puts("should be 3"); + rc = print_list(); + if (s.cnt != 3 || rc !=3) { + puts("test count is wrong"); + return 1; + } + + n.str = strdup("test4"); + n.key = NULL; + n.hits = 1; + slist_append(&s, &n); + puts("should add a #4"); + rc = print_list(); + if (s.cnt != 4 || rc != 4) { + puts("test count is wrong"); + return 1; + } + + slist_add_if_uniq(&s, "test2"); + puts("should be same"); + rc = print_list(); + if (s.cnt != 4 || rc != 4) { + puts("test count is wrong"); + return 1; + } + + slist_clear(&s); + puts("should be empty"); + rc = print_list(); + if (s.cnt != 0 || rc != 0) { + puts("test count is wrong"); + return 1; + } + puts("starting sort test"); + + // Now test to see if the sort function works + // Fill the list exactly backwards + slist_add_if_uniq(&s, "test3"); + slist_add_if_uniq(&s, "test3"); + slist_add_if_uniq(&s, "test4"); + slist_add_if_uniq(&s, "test3"); + slist_add_if_uniq(&s, "test4"); + slist_add_if_uniq(&s, "test2"); + slist_add_if_uniq(&s, "test4"); + slist_add_if_uniq(&s, "test2"); + slist_add_if_uniq(&s, "test4"); + slist_add_if_uniq(&s, "test1"); + + slist_sort_by_hits(&s); + slist_first(&s); + do { + node = slist_get_cur(&s); + if (node->hits != (4-i)) { + printf("Sort test failed - i:%d != hits:%d\n", i, node->hits); + return 1; + } + i++; + } while ((node = slist_next(&s))); + puts("sort test passes"); + + slist_clear(&s); + + return 0; +} + diff --git a/framework/src/audit/tools/Makefile.am b/framework/src/audit/tools/Makefile.am new file mode 100644 index 00000000..15ba254f --- /dev/null +++ b/framework/src/audit/tools/Makefile.am @@ -0,0 +1,26 @@ +# Makefile.am -- +# Copyright 2008 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig + +SUBDIRS = aulast aulastlog ausyscall auvirt + diff --git a/framework/src/audit/tools/aulast/Makefile.am b/framework/src/audit/tools/aulast/Makefile.am new file mode 100644 index 00000000..b20987f9 --- /dev/null +++ b/framework/src/audit/tools/aulast/Makefile.am @@ -0,0 +1,34 @@ +# Makefile.am -- +# Copyright 2008,2010,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +EXTRA_DIST = $(man_MANS) +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib -I${top_srcdir}/auparse +LIBS = -L${top_builddir}/auparse -lauparse +AM_CFLAGS = -D_GNU_SOURCE +bin_PROGRAMS = aulast +noinst_HEADERS = aulast-llist.h +man_MANS = aulast.8 + +aulast_SOURCES = aulast.c aulast-llist.c + diff --git a/framework/src/audit/tools/aulast/aulast-llist.c b/framework/src/audit/tools/aulast/aulast-llist.c new file mode 100644 index 00000000..be05e976 --- /dev/null +++ b/framework/src/audit/tools/aulast/aulast-llist.c @@ -0,0 +1,196 @@ +/* +* aulast-llist.c - Minimal linked list library +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include +#include +#include "aulast-llist.h" + +void list_create(llist *l) +{ + l->head = NULL; + l->cur = NULL; +} + +lnode *list_next(llist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +static void list_append(llist *l, lnode *node) +{ + node->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = node; + else if (l->cur) { + // Make sure we are at the end + while (l->cur->next) + l->cur = l->cur->next; + + l->cur->next = node; + } + + // make newnode current + l->cur = node; +} + +void list_clear(llist* l) +{ + lnode* nextnode; + register lnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free((void *)current->name); + free((void *)current->term); + free((void *)current->host); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; +} + +int list_create_session(llist *l, uid_t auid, int pid, int session, + unsigned long serial) +{ + lnode *n = malloc(sizeof(lnode)); + if (n == NULL) + return 0; + n->session = session; + n->start = 0; + n->end = 0; + n->auid = auid; + n->pid = pid; + n->result = -1; + n->name = NULL; + n->term = NULL; + n->host = NULL; + n->status = LOG_IN; + n->loginuid_proof = serial; + n->user_login_proof = 0; + n->user_end_proof = 0; + list_append(l, n); + return 1; +} + +int list_update_start(llist* l, time_t start, const char *host, + const char *term, int res, unsigned long serial) +{ + register lnode* cur; + if (l == NULL) + return 0; + + cur=list_get_cur(l); + cur->start = start; + cur->status = SESSION_START; + if (term) + cur->term = strdup(term); + if (host) + cur->host = strdup(host); + cur->result = res; + cur->user_login_proof = serial; + return 1; +} + +int list_update_logout(llist* l, time_t t, unsigned long serial) +{ + register lnode* cur; + if (l == NULL) + return 0; + + cur=list_get_cur(l); + cur->end = t; + cur->status = LOG_OUT; + cur->user_end_proof = serial; + return 1; +} + +lnode *list_delete_cur(llist *l) +{ + register lnode *cur, *prev; + + prev = cur = l->head; /* start at the beginning */ + while (cur) { + if (cur == l->cur) { + if (cur == prev && cur == l->head) { + l->head = cur->next; + l->cur = cur->next; + free((void *)cur->name); + free((void *)cur->term); + free((void *)cur->host); + free(cur); + prev = NULL; + } else { + prev->next = cur->next; + free((void *)cur->name); + free((void *)cur->term); + free((void *)cur->host); + free(cur); + l->cur = prev; + } + return prev; + } else { + prev = cur; + cur = cur->next; + } + } + return NULL; +} + +lnode *list_find_auid(llist *l, uid_t auid, int pid, unsigned int session) +{ + register lnode* cur; + + cur = l->head; /* start at the beginning */ + while (cur) { + if (cur->pid == pid && cur->auid == auid && + cur->session == session) { + l->cur = cur; + return cur; + } else + cur = cur->next; + } + return NULL; +} + +lnode *list_find_session(llist *l, unsigned int session) +{ + register lnode* cur; + + cur = l->head; /* start at the beginning */ + while (cur) { + if (cur->session == session) { + l->cur = cur; + return cur; + } else + cur = cur->next; + } + return NULL; +} + diff --git a/framework/src/audit/tools/aulast/aulast-llist.h b/framework/src/audit/tools/aulast/aulast-llist.h new file mode 100644 index 00000000..a56cace2 --- /dev/null +++ b/framework/src/audit/tools/aulast/aulast-llist.h @@ -0,0 +1,75 @@ +/* +* aulast-llist.h - Header file for aulastlog-llist.c +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AULASTLIST_HEADER +#define AULASTLIST_HEADER + +#include + + +typedef enum { LOG_IN, SESSION_START, LOG_OUT, DOWN, CRASH, GONE } status_t; + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _lnode{ + unsigned int session; // The kernel login session id + time_t start; // first time uid logged in + time_t end; // last time uid logged in + uid_t auid; // user ID + int pid; // pid of program logging in + const char *name; // user name + const char *term; // terminal name + const char *host; // host where logging in from + int result; // login results + status_t status; // Current status of this session + unsigned long loginuid_proof; // audit serial number for loginuid change + unsigned long user_login_proof; // audit serial number for user login event + unsigned long user_end_proof; // audit serial number for user log out event + struct _lnode* next; // Next node pointer +} lnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + lnode *head; // List head + lnode *cur; // Pointer to current node +} llist; + +void list_create(llist *l); +static inline void list_first(llist *l) { l->cur = l->head; } +lnode *list_next(llist *l); +static inline lnode *list_get_cur(llist *l) { return l->cur; } +void list_clear(llist* l); +int list_create_session(llist* l, uid_t auid, int pid, int session, + unsigned long serial); +int list_update_start(llist* l, time_t start, const char *host, + const char *term, int res, unsigned long serial); +int list_update_logout(llist* l, time_t t, unsigned long serial); +lnode *list_delete_cur(llist *l); + +/* Given a uid, find that record. */ +lnode *list_find_auid(llist *l, uid_t auid, int pid, unsigned int session); +lnode *list_find_session(llist *l, unsigned int session); + +#endif + diff --git a/framework/src/audit/tools/aulast/aulast.8 b/framework/src/audit/tools/aulast/aulast.8 new file mode 100644 index 00000000..25705f95 --- /dev/null +++ b/framework/src/audit/tools/aulast/aulast.8 @@ -0,0 +1,47 @@ +.TH AULAST: "8" "Nov 2008" "Red Hat" "System Administration Utilities" +.SH NAME +aulast \- a program similar to last +.SH SYNOPSIS +.B aulast [ options ] [ user ] [ tty ] + +.SH DESCRIPTION +\fBaulast\fP is a program that prints out a listing of the last logged in users similarly to the program \fBlast\fP and \fBlastb\fP. Aulast searches back through the audit logs or the given audit log file and displays a list of all users logged in (and out) based on the range of time in the audit logs. Names of users and tty’s can be given, in which case aulast will show only those entries matching the arguments. Names of ttys can be abbreviated, thus aulast 0 is the same as last tty0. + +The pseudo user reboot logs in each time the system is rebooted. Thus last reboot will show a log of all reboots since the log file was created. + +The main difference that a user will notice is that \fBaulast\fP print events from oldest to newest, while \fBlast\fP prints records from newest to oldest. Also, the audit system is not notified each time a tty or pty is allocated, so you may not see quite as many records indicating users and their tty's. + +.SH OPTIONS +.TP +.B \-\-bad +Report on the bad logins. + +.TP +.B \-\-extract +Write raw audit records used to create the displayed report into a file aulast.log in the current working directory. + +.TP +.BI \-f file +Use the file instead of the audit logs for input. + +.TP +.B \-\-proof +Print out the audit event serial numbers used to determine the preceding line of the report. A Serial number of 0 is a place holder and not an actual event serial number. The serial numbers can be used to examine the actual audit records in more detail. Also an ausearch query is printed that will let you find the audit records associated with that session. + +.TP +.B \-\-stdin +Take audit records from stdin. + +.SH "EXAMPLES" +.nf +To see this month's logins +.B ausearch \-\-start this-month \-\-raw | aulast \-\-stdin + +.SH "SEE ALSO" +.BR last (1), +.BR lastb (1), +.BR ausearch (8), +.BR aureport (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/tools/aulast/aulast.c b/framework/src/audit/tools/aulast/aulast.c new file mode 100644 index 00000000..00ae055a --- /dev/null +++ b/framework/src/audit/tools/aulast/aulast.c @@ -0,0 +1,610 @@ +/* + * aulast.c - A last program based on audit logs + * Copyright (c) 2008-2009,2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This software may be freely redistributed and/or modified under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include "libaudit.h" +#include "auparse.h" +#include "aulast-llist.h" + + +static llist l; +static FILE *f = NULL; +static char *kernel = NULL; + +/* command line params */ +static int cuid = -1, bad = 0, proof = 0, debug = 0; +static char *cterm = NULL; + +void usage(void) +{ + fprintf(stderr, + "usage: aulast [--stdin] [--proof] [--extract] [-f file] [user name] [tty]\n"); +} + +/* This outputs a line of text reporting the login/out times */ +static void report_session(lnode* cur) +{ + int notime = 0; + + // Don't list failed logins + if (cur == NULL) + return; + + if (cur->result != bad) + return; + + if (cur->name) { + // This is a reboot record + printf("%-8.8s ", cur->name); + if (cur->end == 0) { + cur->end = time(NULL); + notime = 1; + } + } else { + struct passwd *p = getpwuid(cur->auid); + if (p) + printf("%-8.8s ", p->pw_name); + else + printf("%-8.u ", cur->auid); + } + if (strncmp("/dev/", cur->term, 5) == 0) + printf("%-12.12s ", cur->term+5); + else + printf("%-12.12s ", cur->term); + printf("%-16.16s ", cur->host ? cur->host : "?"); + printf("%-16.16s ", ctime(&cur->start)); + switch(cur->status) + { + case SESSION_START: + printf(" still logged in\n"); + break; + case DOWN: + printf("- down\n"); + break; + case CRASH: + printf("- crash\n"); + break; + case GONE: + printf(" gone - no logout\n"); + break; + case LOG_OUT: { + time_t secs; + int mins, hours, days; + if (notime) + printf("- %-7.5s", " "); + else + printf("- %-7.5s", ctime(&cur->end) + 11); + secs = cur->end - cur->start; + mins = (secs / 60) % 60; + hours = (secs / 3600) % 24; + days = secs / 86400; + if (days) + printf("(%d+%02d:%02d)\n", days, hours, mins); + else + printf("(%02d:%02d)\n", hours, mins); + } + break; + default: + printf("\n"); + break; + } + if (proof) { + char start[32], end[32]; + struct tm *btm; + + if (cur->loginuid_proof == 0 && cur->result == 1) // Bad login + printf(" audit event proof serial number:" + " %lu\n", cur->user_login_proof); + else + printf(" audit event proof serial numbers:" + " %lu, %lu, %lu\n", cur->loginuid_proof, + cur->user_login_proof, cur->user_end_proof); + printf(" Session data can be found with this search:\n"); + btm = localtime(&cur->start); + strftime(start, sizeof(start), "%x %T", btm); + if (cur->end != 0) { + btm = localtime(&cur->end); + strftime(end, sizeof(end), "%x %T", btm); + printf(" ausearch --start %s --end %s", + start, end); + } else { + printf(" ausearch --start %s", start); + } + if (cur->name == NULL) + printf(" --session %d", cur->session); + if (cur->loginuid_proof == 0 && cur->result == 1) // Bad login + printf(" -a %lu", cur->user_login_proof); + printf("\n\n"); + } +} + +static void extract_record(auparse_state_t *au) +{ + if (f == NULL) + return; + + fprintf(f, "%s\n", auparse_get_record_text(au)); +} + +static void create_new_session(auparse_state_t *au) +{ + const char *tpid, *tses, *tauid; + int pid = -1, auid = -1, ses = -1; + lnode *cur; + + // Get pid + tpid = auparse_find_field(au, "pid"); + if (tpid) + pid = auparse_get_field_int(au); + + // Get second auid field + tauid = auparse_find_field(au, "old-auid"); + if (tauid) + tauid = auparse_find_field(au, "auid"); + else { // kernel 3.13 or older + auparse_first_record(au); + auparse_find_field(au, "auid"); + auparse_next_field(au); + tauid = auparse_find_field(au, "auid"); + } + if (tauid) + auid = auparse_get_field_int(au); + + // Get second ses field + tses = auparse_find_field(au, "old-ses"); + if (tses) + tses = auparse_find_field(au, "ses"); + else { // kernel 3.13 or older + auparse_first_record(au); + auparse_find_field(au, "ses"); + auparse_next_field(au); + tses = auparse_find_field(au, "ses"); + } + if (tses) + ses = auparse_get_field_int(au); + + // Check that they are valid + if (pid == -1 || auid ==-1 || ses == -1) { + if (debug) + fprintf(stderr, "Bad login for event: %lu\n", + auparse_get_serial(au)); + return; + } + + // See if this session is already open + //cur = list_find_auid(&l, auid, pid, ses); + cur = list_find_session(&l, ses); + if (cur) { + // This means we have an open session close it out + cur->status = GONE; + cur->end = auparse_get_time(au); + report_session(cur); + list_delete_cur(&l); + } + + // If this is supposed to be limited to a specific + // uid and we don't have that record, skip creating it + if (cuid != -1 && cuid != auid) { + if (debug) + fprintf(stderr, + "login reporting limited to %d for event: %lu\n", + cuid, auparse_get_serial(au)); + return; + } + + list_create_session(&l, auid, pid, ses, auparse_get_serial(au)); +} + +static void update_session_login(auparse_state_t *au) +{ + const char *tpid, *tses, *tuid, *tacct=NULL, *host, *term, *tres; + int pid = -1, uid = -1, ses = -1, result = -1; + time_t start; + lnode *cur; + + // Get pid + tpid = auparse_find_field(au, "pid"); + if (tpid) + pid = auparse_get_field_int(au); + + // Get ses field - skipping first uid + tses = auparse_find_field(au, "ses"); + if (tses) + ses = auparse_get_field_int(au); + + // Get second uid field - we should be positioned past the first one + // gdm sends uid, everything else sends id, we try acct as last resort + tuid = auparse_find_field(au, "uid"); + if (tuid) + uid = auparse_get_field_int(au); + else { + auparse_first_record(au); + tuid = auparse_find_field(au, "id"); + if (tuid) + uid = auparse_get_field_int(au); + else { + auparse_first_record(au); + tuid = auparse_find_field(au, "acct"); + if (tuid) { + const char *tacct = auparse_interpret_field(au); + struct passwd *pw = getpwnam (tacct); + if (pw != NULL) + uid = pw->pw_uid; + } else + auparse_first_record(au); + } + } + + start = auparse_get_time(au); + + host = auparse_find_field(au, "hostname"); + if (host && strcmp(host, "?") == 0) + host = auparse_find_field(au, "addr"); + + term = auparse_find_field(au, "terminal"); + if (term == NULL) + term = "?"; + tres = auparse_find_field(au, "res"); + if (tres) + tres = auparse_interpret_field(au); + if (tres) { + if (strcmp(tres, "success") == 0) + result = 0; + else + result = 1; + } + // We only get tacct when its a bad login + if (result == 1) { + auparse_first_record(au); + tacct = auparse_find_field(au, "acct"); + if (tacct) + tacct = auparse_interpret_field(au); + } else { + // Check that they are valid + if (pid == -1 || uid ==-1 || ses == -1) { + if (debug) + fprintf(stderr, + "Bad user login for event: %lu\n", + auparse_get_serial(au)); + return; + } + } + + // See if this session is already open + if (result == 0) + cur = list_find_auid(&l, uid, pid, ses); + else + cur = NULL; + if (cur) { + // If we are limited to a specific terminal and + // we find out the session is not associated with + // the terminal of interest, delete the current node + if (cterm && strstr(term, cterm) == NULL) { + list_delete_cur(&l); + if (debug) + fprintf(stderr, + "User login limited to %s for event: %lu\n", + cterm, auparse_get_serial(au)); + return; + } + + // This means we have an open session - update it + list_update_start(&l, start, host, term, result, + auparse_get_serial(au)); + + // If the results were failed, we can close it out + /* FIXME: result cannot be true. This is dead code. + if (result) { + report_session(cur); + list_delete_cur(&l); + } */ + } else if (bad == 1 && result == 1) { + // If it were a bad login and we are wanting bad logins + // create the record and report it. + lnode n; + + n.start = start; + n.end = start; + n.auid = uid; + n.name = tacct; + n.term = term; + n.host = host; + n.result = result; + n.status = LOG_OUT; + n.loginuid_proof = 0; + n.user_login_proof = auparse_get_serial(au); + n.user_end_proof = 0; + report_session(&n); + } else if (debug) + printf("Session not found or updated\n"); +} + +static void update_session_logout(auparse_state_t *au) +{ + const char *tses, *tauid, *tpid; + int pid = -1, auid = -1, ses = -1; + lnode *cur; + + // Get pid field + tpid = auparse_find_field(au, "pid"); + if (tpid) + pid = auparse_get_field_int(au); + + // Get auid field + tauid = auparse_find_field(au, "auid"); + if (tauid) + auid = auparse_get_field_int(au); + + // Get ses field + tses = auparse_find_field(au, "ses"); + if (tses) + ses = auparse_get_field_int(au); + + // Check that they are valid + if (pid == -1 || auid ==-1 || ses == -1) { + if (debug) + fprintf(stderr, "Bad user logout for event: %lu\n", + auparse_get_serial(au)); + return; + } + + // See if this session is already open + cur = list_find_auid(&l, auid, pid, ses); + if (cur) { + // if time never got updated, this must be a cron or su + // session...so we will just delete it. + if (cur->start) { + // This means we have an open session close it out + time_t end = auparse_get_time(au); + list_update_logout(&l, end, auparse_get_serial(au)); + report_session(cur); + } else if (debug) + fprintf(stderr, "start time error for event: %lu\n", + auparse_get_serial(au)); + list_delete_cur(&l); + } +} + +static void process_bootup(auparse_state_t *au) +{ + lnode *cur; + int start; + + // See if we have unclosed boot up and make into CRASH record + list_first(&l); + cur = list_get_cur(&l); + while (cur) { + if (cur->name) { + cur->user_end_proof = auparse_get_serial(au); + cur->status = CRASH; + cur->end = auparse_get_time(au); + report_session(cur); + } + cur = list_next(&l); + } + + // Logout and process anyone still left in the machine + list_first(&l); + cur = list_get_cur(&l); + while (cur) { + if (cur->status != CRASH) { + cur->user_end_proof = auparse_get_serial(au); + cur->status = DOWN; + cur->end = auparse_get_time(au); + report_session(cur); + } + cur = list_next(&l); + } + + // Since this is a boot message, all old entries should be gone + list_clear(&l); + list_create(&l); + + // make reboot record - user:reboot, tty:system boot, host: kernel + start = auparse_get_time(au); + list_create_session(&l, 0, 0, 0, auparse_get_serial(au)); + cur = list_get_cur(&l); + cur->start = start; + cur->name = strdup("reboot"); + cur->term = strdup("system boot"); + if (kernel) + cur->host = strdup(kernel); + cur->result = 0; +} + +static void process_kernel(auparse_state_t *au) +{ + const char *kernel_str = auparse_find_field(au, "kernel"); + if (kernel_str == NULL) + return; + + free(kernel); + kernel = strdup(kernel_str); +} + +static void process_shutdown(auparse_state_t *au) +{ + lnode *cur; + + // Find reboot record + list_first(&l); + cur = list_get_cur(&l); + while (cur) { + if (cur->name) { + // Found it - close it out and display it + time_t end = auparse_get_time(au); + list_update_logout(&l, end, auparse_get_serial(au)); + report_session(cur); + list_delete_cur(&l); + return; + } + cur = list_next(&l); + } +} + +int main(int argc, char *argv[]) +{ + int i, use_stdin = 0; + char *user = NULL, *file = NULL; + struct passwd *p; + auparse_state_t *au; + + setlocale (LC_ALL, ""); + for (i=1; ipw_uid; + user = argv[i]; + continue; + } + } + if (cterm == NULL) { + cterm = argv[i]; + } else { + usage(); + return 1; + } + } else { + if (strcmp(argv[i], "-f") == 0) { + if (use_stdin == 0) { + i++; + file = argv[i]; + } else { + fprintf(stderr,"stdin already given\n"); + return 1; + } + } else if (strcmp(argv[i], "--bad") == 0) { + bad = 1; + } else if (strcmp(argv[i], "--proof") == 0) { + proof = 1; + } else if (strcmp(argv[i], "--extract") == 0) { + f = fopen("aulast.log", "wt"); + } else if (strcmp(argv[i], "--stdin") == 0) { + if (file == NULL) + use_stdin = 1; + else { + fprintf(stderr, "file already given\n"); + return 1; + } + } else if (strcmp(argv[i], "--debug") == 0) { + debug = 1; + } else { + usage(); + return 1; + } + } + } + list_create(&l); + + // Search for successful user logins + if (file) + au = auparse_init(AUSOURCE_FILE, file); + else if (use_stdin) + au = auparse_init(AUSOURCE_FILE_POINTER, stdin); + else { + if (getuid()) { + fprintf(stderr, "You probably need to be root for this to work\n"); + } + au = auparse_init(AUSOURCE_LOGS, NULL); + } + if (au == NULL) { + fprintf(stderr, "Error - %s\n", strerror(errno)); + goto error_exit_1; + } + + // The theory: iterate though events + // 1) when LOGIN is found, create a new session node + // 2) if that session number exists, close out the old one + // 3) when USER_LOGIN is found, update session node + // 4) When USER_END is found update session node and close it out + // 5) When BOOT record found make new record and check for previous + // 6) If previous boot found, set status to crash and logout everyone + // 7) When SHUTDOWN found, close out reboot record + + while (auparse_next_event(au) > 0) { + // We will take advantage of the fact that all events + // of interest are one record long + int type = auparse_get_type(au); + if (type < 0) + continue; + switch (type) + { + case AUDIT_LOGIN: + create_new_session(au); + extract_record(au); + break; + case AUDIT_USER_LOGIN: + update_session_login(au); + extract_record(au); + break; + case AUDIT_USER_END: + update_session_logout(au); + extract_record(au); + break; + case AUDIT_SYSTEM_BOOT: + process_bootup(au); + extract_record(au); + break; + case AUDIT_SYSTEM_SHUTDOWN: + process_shutdown(au); + extract_record(au); + break; + case AUDIT_DAEMON_START: + process_kernel(au); + extract_record(au); + break; + } + } + auparse_destroy(au); + + // Now output the leftovers + list_first(&l); + do { + lnode *cur = list_get_cur(&l); + report_session(cur); + } while (list_next(&l)); + + free(kernel); + list_clear(&l); + if (f) + fclose(f); + return 0; + +error_exit_1: + list_clear(&l); + if (f) + fclose(f); + return 1; +} + diff --git a/framework/src/audit/tools/aulastlog/Makefile.am b/framework/src/audit/tools/aulastlog/Makefile.am new file mode 100644 index 00000000..5c2403a9 --- /dev/null +++ b/framework/src/audit/tools/aulastlog/Makefile.am @@ -0,0 +1,34 @@ +# Makefile.am -- +# Copyright 2008,2010,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +EXTRA_DIST = $(man_MANS) +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/auparse +LIBS = -L${top_builddir}/auparse -lauparse +AM_CFLAGS = -D_GNU_SOURCE +bin_PROGRAMS = aulastlog +noinst_HEADERS = aulastlog-llist.h +man_MANS = aulastlog.8 + +aulastlog_SOURCES = aulastlog.c aulastlog-llist.c + diff --git a/framework/src/audit/tools/aulastlog/aulastlog-llist.c b/framework/src/audit/tools/aulastlog/aulastlog-llist.c new file mode 100644 index 00000000..25242b00 --- /dev/null +++ b/framework/src/audit/tools/aulastlog/aulastlog-llist.c @@ -0,0 +1,148 @@ +/* +* aulastlog-llist.c - Minimal linked list library +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#include +#include +#include "aulastlog-llist.h" + +void list_create(llist *l) +{ + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +lnode *list_next(llist *l) +{ + if (l->cur == NULL) + return NULL; + l->cur = l->cur->next; + return l->cur; +} + +void list_append(llist *l, lnode *node) +{ + lnode* newnode; + + newnode = malloc(sizeof(lnode)); + + newnode->sec = node->sec; + newnode->uid = node->uid; + newnode->name = strdup(node->name); + if (node->host) + newnode->host = strdup(node->host); + else + newnode->host = NULL; + if (node->term) + newnode->term = strdup(node->term); + else + newnode->term = NULL; + newnode->item = l->cnt; + newnode->next = NULL; + + // if we are at top, fix this up + if (l->head == NULL) + l->head = newnode; + else // Otherwise add pointer to newnode + l->cur->next = newnode; + + // make newnode current + l->cur = newnode; + l->cnt++; +} + +void list_clear(llist* l) +{ + lnode* nextnode; + register lnode* current; + + current = l->head; + while (current) { + nextnode=current->next; + free(current->name); + free(current->host); + free(current->term); + free(current); + current=nextnode; + } + l->head = NULL; + l->cur = NULL; + l->cnt = 0; +} + +int list_update_login(llist* l, time_t t) +{ + register lnode* cur; + if (l == NULL) + return 0; + + cur=list_get_cur(l); + cur->sec = t; + return 1; +} + +int list_update_host(llist* l, const char *h) +{ + register lnode* cur; + if (l == NULL) + return 0; + + cur=list_get_cur(l); + if (h) { + free(cur->host); + cur->host = strdup(h); + } else + cur->host = NULL; + return 1; +} + +int list_update_term(llist* l, const char *t) +{ + register lnode* cur; + if (l == NULL) + return 0; + + cur=list_get_cur(l); + if (t) { + free(cur->term); + cur->term = strdup(t); + } else + cur->term = NULL; + return 1; +} + +lnode *list_find_uid(llist *l, uid_t uid) +{ + register lnode* window; + + window = l->head; /* start at the beginning */ + while (window) { + if (window->uid == uid) { + l->cur = window; + return window; + } else + window = window->next; + } + return NULL; +} + diff --git a/framework/src/audit/tools/aulastlog/aulastlog-llist.h b/framework/src/audit/tools/aulastlog/aulastlog-llist.h new file mode 100644 index 00000000..ea965425 --- /dev/null +++ b/framework/src/audit/tools/aulastlog/aulastlog-llist.h @@ -0,0 +1,65 @@ +/* +* aulastlog-llist.h - Header file for aulastlog-llist.c +* Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. +* All Rights Reserved. +* +* This software may be freely redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free +* Software Foundation; either version 2, or (at your option) any +* later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. If not, write to the +* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Authors: +* Steve Grubb +*/ + +#ifndef AULASTLIST_HEADER +#define AULASTLIST_HEADER + +#include + + +/* This is the node of the linked list. message & item are the only elements + * at this time. Any data elements that are per item goes here. */ +typedef struct _lnode{ + time_t sec; // last time uid logged in + uid_t uid; // user ID + char *name; // users name + char *host; // host where logging in from + char *term; // terminal name + unsigned int item; // Which item of the same event + struct _lnode* next; // Next node pointer +} lnode; + +/* This is the linked list head. Only data elements that are 1 per + * event goes here. */ +typedef struct { + lnode *head; // List head + lnode *cur; // Pointer to current node + unsigned int cnt; // How many items in this list +} llist; + +void list_create(llist *l); +static inline void list_first(llist *l) { l->cur = l->head; } +lnode *list_next(llist *l); +static inline lnode *list_get_cur(llist *l) { return l->cur; } +static inline unsigned int list_get_cnt(llist *l) { return l->cnt; } +void list_append(llist *l, lnode *node); +void list_clear(llist* l); +int list_update_login(llist* l, time_t t); +int list_update_host(llist* l, const char *h); +int list_update_term(llist* l, const char *t); + +/* Given a uid, find that record. */ +lnode *list_find_uid(llist *l, uid_t uid); + +#endif + diff --git a/framework/src/audit/tools/aulastlog/aulastlog.8 b/framework/src/audit/tools/aulastlog/aulastlog.8 new file mode 100644 index 00000000..b8b44c4f --- /dev/null +++ b/framework/src/audit/tools/aulastlog/aulastlog.8 @@ -0,0 +1,24 @@ +.TH AULASTLOG: "8" "Feb 2009" "Red Hat" "System Administration Utilities" +.SH NAME +aulastlog \- a program similar to lastlog +.SH SYNOPSIS +.B aulastlog [ options ] +.SH DESCRIPTION +\fBaulastlog\fP is a program that prints out the last login for all users of a machine similar to the way lastlog does. The login-name, port, and last login time will be printed. + +If the user has never logged in, the message \fB** Never logged in**\fP will be displayed instead of the port and time. + +.SH OPTIONS +.TP +.B \-u, \-\-user +Print the lastlog record for user with specified LOGIN only. +.TP +.B \-\-stdin +Use stdin as the source of audit records. +.SH "SEE ALSO" +.BR lastlog (8), +.BR ausearch (8), +.BR aureport (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/tools/aulastlog/aulastlog.c b/framework/src/audit/tools/aulastlog/aulastlog.c new file mode 100644 index 00000000..c51b1efb --- /dev/null +++ b/framework/src/audit/tools/aulastlog/aulastlog.c @@ -0,0 +1,169 @@ +/* + * aulastlog.c - A lastlog program based on audit logs + * Copyright (c) 2008-2009,2011 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This software may be freely redistributed and/or modified under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: + * Steve Grubb + */ + +#include +#include +#include +#include +#include +#include "auparse.h" +#include "aulastlog-llist.h" + +void usage(void) +{ + fprintf(stderr, "usage: aulastlog [--stdin] [--user name]\n"); +} + +int main(int argc, char *argv[]) +{ + int i, use_stdin = 0; + char *user = NULL; + struct passwd *p; + auparse_state_t *au; + llist l; + + setlocale (LC_ALL, ""); + for (i=1; ipw_uid; + n.name = p->pw_name; + n.host = NULL; + n.term = NULL; + if (user == NULL) + list_append(&l, &n); + else if (strcmp(user, p->pw_name) == 0) + list_append(&l, &n); + } + endpwent(); + + if (user && list_get_cnt(&l) == 0) { + printf("Unknown User: %s\n", user); + return 1; + } + + // Search for successful user logins + if (use_stdin) + au = auparse_init(AUSOURCE_FILE_POINTER, stdin); + else + au = auparse_init(AUSOURCE_LOGS, NULL); + if (au == NULL) { + printf("Error - %s\n", strerror(errno)); + goto error_exit_1; + } + if (ausearch_add_item(au, "type", "=", "USER_LOGIN", + AUSEARCH_RULE_CLEAR)){ + printf("ausearch_add_item error - %s\n", strerror(errno)); + goto error_exit_2; + } + if (ausearch_add_item(au, "res", "=", "success", + AUSEARCH_RULE_AND)){ + printf("ausearch_add_item error - %s\n", strerror(errno)); + goto error_exit_2; + } + if (ausearch_set_stop(au, AUSEARCH_STOP_RECORD)){ + printf("ausearch_set_stop error - %s\n", strerror(errno)); + goto error_exit_2; + } + + // Now scan the logs and append events + while (ausearch_next_event(au) > 0) { + const au_event_t *e = auparse_get_timestamp(au); + if (auparse_find_field(au, "auid")) { + uid_t u = auparse_get_field_int(au); + list_first(&l); + if (list_find_uid(&l, u)) { + const char *str; + + list_update_login(&l, e->sec); + str = auparse_find_field(au, "hostname"); + if (str) + list_update_host(&l, str); + str = auparse_find_field(au, "terminal"); + if (str) + list_update_term(&l, str); + } + } + if (auparse_next_event(au) < 0) + break; + } + auparse_destroy(au); + + // Now output the report + printf( "Username Port From" + " Latest\n"); + list_first(&l); + do { + char tmp[48]; + const char *c, *h, *t; + lnode *cur = list_get_cur(&l); + if (cur->sec == 0) + c = "**Never logged in**"; + else { + struct tm *btm; + + btm = localtime(&cur->sec); + strftime(tmp, sizeof(tmp), "%x %T", btm); + c = tmp; + } + h = cur->host; + if (h == NULL) + h = ""; + t = cur->term; + if (t == NULL) + t = ""; + printf("%-16s %-12.12s %-26.26s %s\n", cur->name, t, h, c); + } while (list_next(&l)); + + list_clear(&l); + return 0; + +error_exit_2: + auparse_destroy(au); +error_exit_1: + list_clear(&l); + return 1; +} + diff --git a/framework/src/audit/tools/ausyscall/Makefile.am b/framework/src/audit/tools/ausyscall/Makefile.am new file mode 100644 index 00000000..6ad983e5 --- /dev/null +++ b/framework/src/audit/tools/ausyscall/Makefile.am @@ -0,0 +1,32 @@ +# Makefile.am -- +# Copyright 2008,2015 Red Hat Inc., Durham, North Carolina. +# All Rights Reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Authors: +# Steve Grubb +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +EXTRA_DIST = $(man_MANS) +AM_CPPFLAGS = -I${top_srcdir} -I${top_srcdir}/lib +LIBS = -L${top_builddir}/lib -laudit +bin_PROGRAMS = ausyscall +man_MANS = ausyscall.8 + +ausyscall_SOURCES = ausyscall.c +ausyscall_CFLAGS = -g -D_GNU_SOURCE diff --git a/framework/src/audit/tools/ausyscall/ausyscall.8 b/framework/src/audit/tools/ausyscall/ausyscall.8 new file mode 100644 index 00000000..16f9196e --- /dev/null +++ b/framework/src/audit/tools/ausyscall/ausyscall.8 @@ -0,0 +1,34 @@ +.TH AUSYSCALL: "8" "Nov 2008" "Red Hat" "System Administration Utilities" +.SH NAME +ausyscall \- a program that allows mapping syscall names and numbers +.SH SYNOPSIS +.B ausyscall [arch] name | number | \-\-dump | \-\-exact +.SH DESCRIPTION +\fBausyscall\fP is a program that prints out the mapping from syscall name to number and reverse for the given arch. The arch can be anything returned by `uname \-m`. If arch is not given, the program will take a guess based on the running image. You may give the syscall name or number and it will find the opposite. You can also dump the whole table with the \-\-dump option. By default a syscall name lookup will be a substring match meaning that it will try to match all occurrences of the given name with syscalls. So giving a name of chown will match both fchown and chown as any other syscall with chown in its name. If this behavior is not desired, pass the \-\-exact flag and it will do an exact string match. + +This program can be used to verify syscall numbers on a biarch platform for rule optimization. For example, suppose you had an auditctl rule: + +.B \-a always, exit \-S open \-F exit=\-EPERM \-k fail\-open + +If you wanted to verify that both 32 and 64 bit programs would be audited, run "ausyscall i386 open" and then "ausyscall x86_64 open". Look at the returned numbers. If they are different, you will have to write two auditctl rules to get complete coverage. + +.nf +.B \-a always,exit \-F arch=b32 \-S open \-F exit=\-EPERM \-k fail\-open +.B \-a always,exit \-F arch=b64 \-S open \-F exit=\-EPERM \-k fail\-open +.fi + +For more information about a specific syscall, use the man program and pass the number 2 as an argument to make sure that you get the syscall information rather than a shell script program or glibc function call of the same name. For example, if you wanted to learn about the open syscall, type: man 2 open. +.SH OPTIONS +.TP +.B \-\-dump +Print all syscalls for the given arch +.TP +.B \-\-exact +Instead of doing a partial word match, match the given syscall name exactly. + +.SH "SEE ALSO" +.BR ausearch (8), +.BR auditctl (8). + +.SH AUTHOR +Steve Grubb diff --git a/framework/src/audit/tools/ausyscall/ausyscall.c b/framework/src/audit/tools/ausyscall/ausyscall.c new file mode 100644 index 00000000..361afd99 --- /dev/null +++ b/framework/src/audit/tools/ausyscall/ausyscall.c @@ -0,0 +1,155 @@ +/* + * ausysvcall.c - A program that lets you map syscall names and numbers + * Copyright (c) 2008 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This software may be freely redistributed and/or modified under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: + * Steve Grubb + */ + +#include +#include +#include +#include +#include "libaudit.h" + +#define LAST_SYSCALL 1400 // IA64 is in the 1300's right now + +void usage(void) +{ + fprintf(stderr, "usage: ausyscall [arch] name | number | --dump | --exact\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int i, rc; + int machine=-1, syscall_num=-1, dump=0, exact=0; + const char *name = NULL; + + if (argc > 4) { + fputs("Too many arguments\n", stderr); + usage(); + } else if (argc < 2) + usage(); + + for (i=1; i +# + +CONFIG_CLEAN_FILES = *.loT *.rej *.orig +AUTOMAKE_OPTIONS = no-dependencies +EXTRA_DIST = $(man_MANS) +AM_CPPFLAGS = -I${top_srcdir} \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/auparse \ + -I${top_srcdir}/src +LIBS = -L${top_builddir}/auparse -lauparse +AM_CFLAGS = -D_GNU_SOURCE +bin_PROGRAMS = auvirt +noinst_HEADERS = auvirt-list.h +man_MANS = auvirt.8 + +auvirt_SOURCES = auvirt.c \ + auvirt-list.c \ + ${top_srcdir}/src/ausearch-time.c diff --git a/framework/src/audit/tools/auvirt/auvirt-list.c b/framework/src/audit/tools/auvirt/auvirt-list.c new file mode 100644 index 00000000..75021889 --- /dev/null +++ b/framework/src/audit/tools/auvirt/auvirt-list.c @@ -0,0 +1,105 @@ +#include "auvirt-list.h" +#include + +list_t *list_init(list_t *list, list_free_data_fn *free_data_fn) +{ + if (list == NULL) + return NULL; + list->head = list->tail = NULL; + list->free_data_fn = free_data_fn; + return list; +} + +list_t *list_new(list_free_data_fn *free_data_fn) +{ + return list_init(malloc(sizeof(list_t)), free_data_fn); +} + +void list_free_node(list_node_t *node, list_free_data_fn *free_data_fn) +{ + if (node) { + if (free_data_fn) + free_data_fn(node->data); + free(node); + } +} + +void list_free_(list_t *list, list_free_data_fn *free_data_fn) +{ + if (list != NULL) { + list_node_t *it = list->head; + while (it && it->next) { + it = it->next; + list_free_node(it->prev, free_data_fn); + } + list_free_node(it, free_data_fn); + free(list); + } +} + +void list_free(list_t *list) +{ + if (list) + list_free_(list, list->free_data_fn); +} + +list_node_t *list_insert_after(list_t *list, list_node_t *it, + void *data) +{ + list_node_t *node = NULL; + if (list == NULL) + return NULL; + + /* allocate node */ + node = malloc(sizeof(list_node_t)); + if (node == NULL) + return NULL; + node->data = data; + + /* insert the new node after it */ + node->prev = it; + if (it) { + node->next = it->next; + it->next = node; + } + else + node->next = list->head; + if (node->next) + node->next->prev = node; + + /* update list's head and tail */ + if (it == list->tail) + list->tail = node; + if (it == NULL) + list->head = node; + + return node; +} + +list_node_t *list_append(list_t *list, void *data) +{ + return list_insert_after(list, list->tail, data); +} + +int list_remove_(list_t *list, list_node_t *it, + list_free_data_fn *free_data_fn) +{ + if (list == NULL || it == NULL) + return 1; + if (list->head == it) + list->head = it->next; + if (list->tail == it) + list->tail = it->prev; + if (it->next) + it->next->prev = it->prev; + if (it->prev) + it->prev->next = it->next; + list_free_node(it, free_data_fn); + return 0; +} + +int list_remove(list_t *list, list_node_t *it) +{ + return list_remove_(list, it, list->free_data_fn); +} + diff --git a/framework/src/audit/tools/auvirt/auvirt-list.h b/framework/src/audit/tools/auvirt/auvirt-list.h new file mode 100644 index 00000000..fb587468 --- /dev/null +++ b/framework/src/audit/tools/auvirt/auvirt-list.h @@ -0,0 +1,31 @@ + +#ifndef AUVIRT_LIST_HEADER +#define AUVIRT_LIST_HEADER + +typedef void (list_free_data_fn)(void *data); + +typedef struct list_node_t { + void *data; + struct list_node_t *prev; + struct list_node_t *next; +} list_node_t; + +typedef struct list_t { + list_node_t *head; + list_node_t *tail; + list_free_data_fn *free_data_fn; +} list_t; + +list_t *list_init(list_t *list, list_free_data_fn *free_data_fn); +list_t *list_new(list_free_data_fn *free_data_fn); +void list_free_(list_t *list, list_free_data_fn *free_data_fn); +void list_free(list_t *list); +list_node_t *list_insert_after(list_t *list, list_node_t *it, + void *data); +list_node_t *list_append(list_t *list, void *data); +int list_remove_(list_t *list, list_node_t *it, + list_free_data_fn *free_data_fn); +int list_remove(list_t *list, list_node_t *it); + +#endif + diff --git a/framework/src/audit/tools/auvirt/auvirt.8 b/framework/src/audit/tools/auvirt/auvirt.8 new file mode 100644 index 00000000..96123f45 --- /dev/null +++ b/framework/src/audit/tools/auvirt/auvirt.8 @@ -0,0 +1,121 @@ +.TH AUVIRT 8 "Dec 2011" "IBM Corp" "System Administration Utilities" +.SH NAME +auvirt - a program that shows data related to virtual machines + +.SH SYNOPSIS +.B auvirt +[ \fIOPTIONS\fP ] + +.SH DESCRIPTION +\fBauvirt\fP shows a list of guest sessions found in the audit logs. If a guest +is specified, only the events related to that guest is considered. To specify a +guest, both UUID or VM name can be given. + +For each guest session the tool prints a record with the domain name, the user +that started the guest, the time when the guest was started and the time when +the guest was stoped. + +If the option "\-\-all\-events" is given a more detailed output is shown. In this +mode other records are shown for guest's stops, resource +assignments, host shutdowns and AVC and anomaly events. The first field +indicates the event type and can have the following values: start, stop, +res, avc, anom and down (for host shutdowns). + +Resource assignments have the additional fields: resource type, reason and +resource. And AVC records have the following additional fields: operation, +result, command and target. + +By default, auvirt reads records from the system audit log file. But +\fB--stdin\fP and \fB--file\fP options can be specified to change this +behavior. + +.SH OPTIONS +.TP +\fB--all-events\fP +Show records for all virtualization related events. +.TP +\fB--debug\fP +Print debug messages to standard output. +.TP +\fB-f\fP, \fB--file\fP \fIfile\fP +Read records from the given \fIfile\fP instead from the system audit log file. +.TP +\fB-h\fP, \fB--help\fP +Print help message and exit. +.TP +\fB--proof\fP +Add after each event a line containing all the identifiers of the audit records +used to calculate the event. Each identifier consists of unix time, +milliseconds and serial number. +.TP +\fB--show-uuid\fP +Add the guest's UUID to each record. +.TP +\fB--stdin\fP +Read records from the standard input instead from the system audit log file. +This option cannot be specified with \fB--file\fP. +.TP +\fB--summary\fP +Print a summary with information about the events found. The summary contains +the considered range of time, the number of guest starts and stops, the number +of resource assignments, the number of AVC and anomaly events, the number of +host shutdowns and the number of failed operations. +.TP +.BR \-te ,\ \-\-end \ [\fIend-date\fP]\ [\fIend-time\fP] +Search for events with time stamps equal to or before the given end time. The +format of end time depends on your locale. If the date is omitted, +.B today +is assumed. If the time is omitted, +.B now +is assumed. Use 24 hour clock time rather than AM or PM to specify time. +An example date using the en_US.utf8 locale is 09/03/2009. An example of time +is 18:00:00. The date format accepted is influenced by the LC_TIME +environmental variable. + +You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP, +\fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP, +\fBthis\-year\fP. \fBToday\fP means starting now. \fBRecent\fP is 10 minutes +ago. \fBYesterday\fP is 1 second after midnight the previous day. +\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week +determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1 +second after midnight on day 1 of the month. \fBThis\-year\fP means the 1 +second after midnight on the first day of the first month. +.TP +.BR \-ts ,\ \-\-start \ [\fIstart-date\fP]\ [\fIstart-time\fP] +Search for events with time stamps equal to or after the given end time. The +format of end time depends on your locale. If the date is omitted, +.B today +is assumed. If the time is omitted, +.B midnight +is assumed. Use 24 hour clock time rather than AM or PM to specify time. An +example date using the en_US.utf8 locale is 09/03/2009. An example of time is +18:00:00. The date format accepted is influenced by the LC_TIME environmental +variable. + +You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP, +\fByesterday\fP, \fBthis\-week\fP, \fBthis\-month\fP, \fBthis\-year\fP. +\fBToday\fP means starting at 1 second after midnight. \fBRecent\fP is 10 +minutes ago. \fBYesterday\fP is 1 second after midnight the previous day. +\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week +determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1 +second after midnight on day 1 of the month. \fBThis\-year\fP means the 1 +second after midnight on the first day of the first month. +.TP +\fB-u\fP, \fB--uuid\fP \ \fIUUID\fP +Only show events related to the guest with the given UUID. +.TP +\fB-v\fP, \fB--vm\fP \ \fIname\fP +Only show events related to the guest with the given name. + +.SH EXAMPLES +To see all the records in this month for a guest + +\fBauvirt \-\-start this\-month \-\-vm GuestVmName \-\-all\-events\fP + +.SH SEE ALSO +.BR aulast (8), +.BR ausearch (8), +.BR aureport (8). + +.SH AUTHOR +Marcelo Cerri diff --git a/framework/src/audit/tools/auvirt/auvirt.c b/framework/src/audit/tools/auvirt/auvirt.c new file mode 100644 index 00000000..655c4541 --- /dev/null +++ b/framework/src/audit/tools/auvirt/auvirt.c @@ -0,0 +1,1595 @@ +/* + * auvirt.c - A tool to extract data related to virtualization. + * Copyright (c) 2011 IBM Corp. + * All Rights Reserved. + * + * This software may be freely redistributed and/or modified under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: + * Marcelo Henrique Cerri + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "auparse.h" +#include "libaudit.h" +#include "ausearch-time.h" +#include "auvirt-list.h" + +/* Command line parameters */ +static int help_flag = 0; +static int stdin_flag = 0; +static int summary_flag = 0; +static int all_events_flag = 0; +static int uuid_flag = 0; +static int proof_flag = 0; +static const char *vm = NULL; +static const char *uuid = NULL; +static const char *file = NULL; +static int debug = 0; +/* + * The start time and end time given in the command line is stored respectively + * in the variables start_time and end_time that are declared/defined in the + * files ausearch-time.h and ausearch-time.c. These files are reused from the + * ausearch tool source code: + * + * time_t start_time = 0; + * time_t end_time = 0; + */ + +/* List of events */ +enum event_type { + ET_NONE = 0, ET_START, ET_STOP, ET_MACHINE_ID, ET_AVC, ET_RES, ET_ANOM, + ET_DOWN +}; +struct record_id { + time_t time; + unsigned int milli; + unsigned long serial; +}; +struct event { + enum event_type type; + time_t start; + time_t end; + uid_t uid; + char *uuid; + char *name; + int success; + pid_t pid; + /* Fields specific for resource events: */ + char *reason; + char *res_type; + char *res; + /* Fields specific for cgroup resources */ + char *cgroup_class; + char *cgroup_detail; + char *cgroup_acl; + /* Fields specific for machine id events: */ + char *seclevel; + /* Fields specific for avc events: */ + char *avc_result; + char *avc_operation; + char *target; + char *comm; + char *context; + /* Fields to print proof information: */ + struct record_id proof[4]; +}; +list_t *events = NULL; + + +/* Auxiliary functions to allocate and to free events. */ +struct event *event_alloc(void) +{ + struct event *event = malloc(sizeof(struct event)); + if (event) { + /* The new event is initialized with values that represents + * unset values: -1 for uid and pid and 0 (or NULL) for numbers + * and pointers. For example, event->end = 0 represents an + * unfinished event. + */ + memset(event, 0, sizeof(struct event)); + event->uid = -1; + event->pid = -1; + } + return event; +} + +void event_free(struct event *event) +{ + if (event) { + free(event->uuid); + free(event->name); + free(event->reason); + free(event->res_type); + free(event->res); + free(event->avc_result); + free(event->avc_operation); + free(event->seclevel); + free(event->target); + free(event->comm); + free(event->cgroup_class); + free(event->cgroup_detail); + free(event->cgroup_acl); + free(event->context); + free(event); + } +} + +inline char *copy_str(const char *str) +{ + return (str) ? strdup(str) : NULL; +} + +void usage(FILE *output) +{ + fprintf(output, "usage: auvirt [--stdin] [--all-events] [--summary] " + "[--start start-date [start-time]] " + "[--end end-date [end-time]] [--file file-name] " + "[--show-uuid] [--proof] " + "[--uuid uuid] [--vm vm-name]\n"); +} + +/* Parse and check command line arguments */ +int parse_args(int argc, char **argv) +{ + /* Based on http://www.ietf.org/rfc/rfc4122.txt */ + const char *uuid_pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-" + "[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"; + int i, rc = 0; + regex_t uuid_regex; + + if (regcomp(&uuid_regex, uuid_pattern, REG_EXTENDED)) { + fprintf(stderr, "Failed to initialize program.\n"); + return 1; + } + + for (i = 1; i < argc; i++) { + const char *opt = argv[i]; + if (opt[0] != '-') { + fprintf(stderr, "Argument not expected: %s\n", opt); + goto error; + } else if (strcmp("--vm", opt) == 0 || + strcmp("-v", opt) == 0) { + if ((i + 1) >= argc || argv[i + 1][0] == '-') { + fprintf(stderr, "\"%s\" option requires " + "an argument.\n", opt); + goto error; + } + vm = argv[++i]; + } else if (strcmp("--uuid", opt) == 0 || + strcmp("-u", opt) == 0) { + if ((i + 1) >= argc || argv[i + 1][0] == '-') { + fprintf(stderr, "\"%s\" option requires " + "an argument.\n", opt); + goto error; + } + if (regexec(&uuid_regex, argv[i + 1], 0, NULL, 0)) { + fprintf(stderr, "Invalid uuid: %s\n", + argv[i + 1]); + goto error; + } + uuid = argv[++i]; + } else if (strcmp("--all-events", opt) == 0 || + strcmp("-a", opt) == 0) { + all_events_flag = 1; + } else if (strcmp("--summary", opt) == 0 || + strcmp("-s", opt) == 0) { + summary_flag = 1; + } else if (strcmp("--file", opt) == 0 || + strcmp("-f", opt) == 0) { + if ((i + 1) >= argc || argv[i + 1][0] == '-') { + fprintf(stderr, "\"%s\" option requires " + "an argument.\n", opt); + goto error; + } + file = argv[++i]; + } else if (strcmp("--show-uuid", opt) == 0) { + uuid_flag = 1; + } else if (strcmp("--stdin", opt) == 0) { + stdin_flag = 1; + } else if (strcmp("--proof", opt) == 0) { + proof_flag = 1; + } else if (strcmp("--help", opt) == 0 || + strcmp("-h", opt) == 0) { + help_flag = 1; + goto exit; + } else if (strcmp("--start", opt) == 0 || + strcmp("-ts", opt) == 0) { + const char *date, *time = NULL; + if ((i + 1) >= argc || argv[i + 1][0] == '-') { + fprintf(stderr, "\"%s\" option requires at " + "least one argument.\n", opt); + goto error; + } + date = argv[++i]; + if ((i + 1) < argc && argv[i + 1][0] != '-') + time = argv[++i]; + /* This will set start_time */ + if(ausearch_time_start(date, time)) + goto error; + } else if (strcmp("--end", opt) == 0 || + strcmp("-te", opt) == 0) { + const char *date, *time = NULL; + if ((i + 1) >= argc || argv[i + 1][0] == '-') { + fprintf(stderr, "\"%s\" option requires at " + "least one argument.\n", opt); + goto error; + } + date = argv[++i]; + if ((i + 1) < argc && argv[i + 1][0] != '-') + time = argv[++i]; + /* This will set end_time */ + if (ausearch_time_end(date, time)) + goto error; + } else if (strcmp("--debug", opt) == 0) { + debug = 1; + } else { + fprintf(stderr, "Unknown option \"%s\".\n", opt); + goto error; + } + } + + /* Validate conflicting options */ + if (stdin_flag && file) { + fprintf(stderr, "\"--sdtin\" and \"--file\" options " + "must not be specified together.\n"); + goto error; + } + + if (debug) { + fprintf(stderr, "help_flag='%i'\n", help_flag); + fprintf(stderr, "stdin_flag='%i'\n", stdin_flag); + fprintf(stderr, "all_events_flag='%i'\n", all_events_flag); + fprintf(stderr, "summary_flag='%i'\n", summary_flag); + fprintf(stderr, "uuid='%s'\n", uuid ? uuid : "(null)"); + fprintf(stderr, "vm='%s'\n", vm ? vm : "(null)"); + fprintf(stderr, "file='%s'\n", file ? file : "(null)"); + fprintf(stderr, "start_time='%-.16s'\n", (start_time == 0L) ? + "" : ctime(&start_time)); + fprintf(stderr, "end_time='%-.16s'\n", (end_time == 0L) ? + "" : ctime(&end_time)); + } + +exit: + regfree(&uuid_regex); + return rc; +error: + rc = 1; + goto exit; +} + +/* Initialize an auparse_state_t with the correct log source. */ +auparse_state_t *init_auparse(void) +{ + auparse_state_t *au = NULL; + if (stdin_flag) { + au = auparse_init(AUSOURCE_FILE_POINTER, stdin); + } else if (file) { + au = auparse_init(AUSOURCE_FILE, file); + } else { + if (getuid()) { + fprintf(stderr, "You probably need to be root for " + "this to work\n"); + } + au = auparse_init(AUSOURCE_LOGS, NULL); + } + if (au == NULL) { + fprintf(stderr, "Error: %s\n", strerror(errno)); + } + return au; +} + +/* Create a criteria to search for the virtualization related records */ +int create_search_criteria(auparse_state_t *au) +{ + char *error = NULL; + char expr[1024]; + snprintf(expr, sizeof(expr), + "(\\record_type >= %d && \\record_type <= %d)", + AUDIT_FIRST_VIRT_MSG, AUDIT_LAST_VIRT_MSG); + if (ausearch_add_expression(au, expr, &error, AUSEARCH_RULE_CLEAR)) { + fprintf(stderr, "Criteria error: %s\n", error); + free(error); + return 1; + } + if (uuid) { + if (ausearch_add_item(au, "uuid", "=", uuid, + AUSEARCH_RULE_AND)) { + fprintf(stderr, "Criteria error: uuid\n"); + return 1; + } + } + if (vm) { + if (ausearch_add_interpreted_item(au, "vm", "=", vm, + AUSEARCH_RULE_AND)) { + fprintf(stderr, "Criteria error: id\n"); + return 1; + } + } + if (all_events_flag || summary_flag) { + if (ausearch_add_item(au, "type", "=", "AVC", + AUSEARCH_RULE_OR)) { + fprintf(stderr, "Criteria error: AVC\n"); + return 1; + } + if (ausearch_add_item(au, "type", "=", "SYSTEM_SHUTDOWN", + AUSEARCH_RULE_OR)) { + fprintf(stderr, "Criteria error: shutdown\n"); + return 1; + } + snprintf(expr, sizeof(expr), + "(\\record_type >= %d && \\record_type <= %d) ||" + "(\\record_type >= %d && \\record_type <= %d)", + AUDIT_FIRST_ANOM_MSG, AUDIT_LAST_ANOM_MSG, + AUDIT_FIRST_KERN_ANOM_MSG, AUDIT_LAST_KERN_ANOM_MSG); + if (ausearch_add_expression(au, expr, &error, + AUSEARCH_RULE_OR)) { + fprintf(stderr, "Criteria error: %s\n", error); + free(error); + return 1; + } + } + if (start_time) { + if (ausearch_add_timestamp_item(au, ">=", start_time, 0, + AUSEARCH_RULE_AND)) { + fprintf(stderr, "Criteria error: start_time\n"); + return 1; + } + } + if (end_time) { + if (ausearch_add_timestamp_item(au, "<=", end_time, 0, + AUSEARCH_RULE_AND)) { + fprintf(stderr, "Criteria error: end_time\n"); + return 1; + } + } + return 0; +} + +/* Extract the most common fields from virtualization-related records. */ +int extract_virt_fields(auparse_state_t *au, const char **p_uuid, + uid_t *p_uid, time_t *p_time, const char **p_name, + int *p_suc) +{ + const char *field; + auparse_first_record(au); + /* Order matters */ + if (p_uid) { + if (!auparse_find_field(au, field = "uid")) + goto error; + *p_uid = auparse_get_field_int(au); + } + if (p_name) { + if (!auparse_find_field(au, field = "vm")) + goto error; + *p_name = auparse_interpret_field(au); + } + if (p_uuid) { + if (!auparse_find_field(au, field = "uuid")) + goto error; + *p_uuid = auparse_get_field_str(au); + } + if (p_suc) { + const char *res = auparse_find_field(au, field = "res"); + if (res == NULL) + goto error; + *p_suc = (strcmp("success", res) == 0) ? 1 : 0; + } + if (p_time) { + *p_time = auparse_get_time(au); + } + return 0; + +error: + if (debug) { + fprintf(stderr, "Failed to get field \"%s\" for record " + "%ld.%03u:%lu\n", field ? field : "", + auparse_get_time(au), + auparse_get_milli(au), + auparse_get_serial(au)); + } + return 1; +} + +/* Return label and categories from a security context. */ +const char *get_seclevel(const char *seclabel) +{ + /* + * system_u:system_r:svirt_t:s0:c107,c434 + * \____ _____/ + * ' + * level + cat + */ + int c = 0; + for (;seclabel && *seclabel; seclabel++) { + if (*seclabel == ':') + c += 1; + if (c == 3) + return seclabel + 1; + } + return NULL; +} + +int add_proof(struct event *event, auparse_state_t *au) +{ + if (!proof_flag) + return 0; + + size_t i, proof_len = sizeof(event->proof)/sizeof(event->proof[0]); + for (i = 0; i < proof_len; i++) { + if (event->proof[i].time == 0) + break; + } + if (i == proof_len) { + if (debug) + fprintf(stderr, "Failed to add proof.\n"); + return 1; + } + + event->proof[i].time = auparse_get_time(au); + event->proof[i].milli = auparse_get_milli(au); + event->proof[i].serial = auparse_get_serial(au); + return 0; +} + +/* + * machine_id records are used to get the selinux context associated to a + * guest. + */ +int process_machine_id_event(auparse_state_t *au) +{ + uid_t uid; + time_t time; + const char *seclevel, *uuid, *name; + struct event *event; + int success; + + seclevel = get_seclevel(auparse_find_field(au, "vm-ctx")); + if (seclevel == NULL) { + if (debug) + fprintf(stderr, "Security context not found for " + "MACHINE_ID event.\n"); + } + + if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) + return 0; + + event = event_alloc(); + if (event == NULL) + return 1; + event->type = ET_MACHINE_ID; + event->uuid = copy_str(uuid); + event->name = copy_str(name); + event->success = success; + event->seclevel = copy_str(seclevel); + event->uid = uid; + event->start = time; + add_proof(event, au); + if (list_append(events, event) == NULL) { + event_free(event); + return 1; + } + return 0; +} + +int add_start_guest_event(auparse_state_t *au) +{ + struct event *start; + uid_t uid; + time_t time; + const char *uuid, *name; + int success; + list_node_t *it; + + /* Just skip this record if it failed to get some of the fields */ + if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) + return 0; + + /* On failure, loop backwards to update all the resources associated to + * the last session of this guest. When a machine_id or a stop event is + * found the loop can be broken because a machine_id is created at the + * beginning of a session and a stop event indicates a previous + * session. + */ + if (!success) { + for (it = events->tail; it; it = it->prev) { + struct event *event = it->data; + if (event->success && event->uuid && + strcmp(uuid, event->uuid) == 0) { + if (event->type == ET_STOP || + event->type == ET_MACHINE_ID) { + /* An old session found. */ + break; + } else if (event->type == ET_RES && + event->end == 0) { + event->end = time; + add_proof(event, au); + } + } + } + } + + start = event_alloc(); + if (start == NULL) + return 1; + start->type = ET_START; + start->uuid = copy_str(uuid); + start->name = copy_str(name); + start->success = success; + start->uid = uid; + start->start = time; + auparse_first_record(au); + if (auparse_find_field(au, "vm-pid")) + start->pid = auparse_get_field_int(au); + add_proof(start, au); + if (list_append(events, start) == NULL) { + event_free(start); + return 1; + } + return 0; +} + +int add_stop_guest_event(auparse_state_t *au) +{ + list_node_t *it; + struct event *stop, *start = NULL, *event = NULL; + uid_t uid; + time_t time; + const char *uuid, *name; + int success; + + /* Just skip this record if it failed to get some of the fields */ + if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) + return 0; + + /* Loop backwards to find the last start event for the uuid and + * update all resource records related to that guest session. + */ + for (it = events->tail; it; it = it->prev) { + event = it->data; + if (event->success && event->uuid && + strcmp(uuid, event->uuid) == 0) { + if (event->type == ET_START) { + /* If an old session is found it's no longer + * necessary to update the resource records. + */ + if (event->end || start) + break; + /* This is the start event related to the + * current session. */ + start = event; + } else if (event->type == ET_STOP || + event->type == ET_MACHINE_ID) { + /* Old session found. */ + break; + } else if (event->type == ET_RES && event->end == 0) { + /* Update the resource assignments. */ + event->end = time; + add_proof(event, au); + } + } + } + if (start == NULL) { + if (debug) { + fprintf(stderr, "Couldn't find the correlated start " + "record to the stop event.\n"); + } + return 0; + } + + /* Create a new stop event */ + stop = event_alloc(); + if (stop == NULL) + return 1; + stop->type = ET_STOP; + stop->uuid = copy_str(uuid); + stop->name = copy_str(name); + stop->success = success; + stop->uid = uid; + stop->start = time; + auparse_first_record(au); + if (auparse_find_field(au, "vm-pid")) + stop->pid = auparse_get_field_int(au); + add_proof(stop, au); + if (list_append(events, stop) == NULL) { + event_free(stop); + return 1; + } + + /* Update the correlated start event. */ + if (success) { + start->end = time; + add_proof(start, au); + } + return 0; +} + +int process_control_event(auparse_state_t *au) +{ + const char *op; + + op = auparse_find_field(au, "op"); + if (op == NULL) { + if (debug) + fprintf(stderr, "Invalid op field.\n"); + return 0; + } + + if (strcmp("start", op) == 0) { + if (add_start_guest_event(au)) + return 1; + } else if (strcmp("stop", op) == 0) { + if (add_stop_guest_event(au)) + return 1; + } else if (debug) { + fprintf(stderr, "Unknown op: %s\n", op); + } + return 0; +} + +inline int is_resource(const char *res) +{ + if (res == NULL || + res[0] == '\0' || + strcmp("0", res) == 0 || + strcmp("?", res) == 0) + return 0; + return 1; +} + +int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time, + const char *name, int success, const char *reason, + const char *res_type, const char *res) +{ + if (!is_resource(res)) + return 0; + + struct event *event = event_alloc(); + if (event == NULL) + return 1; + event->type = ET_RES; + event->uuid = copy_str(uuid); + event->name = copy_str(name); + event->success = success; + event->reason = copy_str(reason); + event->res_type = copy_str(res_type); + event->res = copy_str(res); + event->uid = uid; + event->start = time; + add_proof(event, au); + + /* Get cgroup specific fields. */ + if (strcmp("cgroup", res_type) == 0) { + event->cgroup_class = copy_str(auparse_find_field(au, "class")); + if (event->cgroup_class) { + const char *detail = NULL; + if (strcmp("path", event->cgroup_class) == 0) { + if (auparse_find_field(au, "path")) + detail = auparse_interpret_field(au); + } else if (strcmp("major", event->cgroup_class) == 0) { + detail = auparse_find_field(au, "category"); + } + event->cgroup_detail = copy_str(detail); + } + event->cgroup_acl = copy_str(auparse_find_field(au, "acl")); + } + + if (list_append(events, event) == NULL) { + event_free(event); + return 1; + } + return 0; +} + +int update_resource(auparse_state_t *au, const char *uuid, uid_t uid, + time_t time, const char *name, int success, const char *reason, + const char *res_type, const char *res) +{ + if (!is_resource(res) || !success) + return 0; + + list_node_t *it; + struct event *start = NULL; + + /* Find the last start event for the uuid */ + for (it = events->tail; it; it = it->prev) { + start = it->data; + if (start->type == ET_RES && + start->success && + start->uuid && + strcmp(uuid, start->uuid) == 0 && + strcmp(res_type, start->res_type) == 0 && + strcmp(res, start->res) == 0) + break; + } + if (it == NULL) { + if (debug) { + fprintf(stderr, "Couldn't find the correlated resource" + " record to update for %s.\n", res_type); + } + return 0; + } + + start->end = time; + add_proof(start, au); + return 0; +} + +int process_resource_event(auparse_state_t *au) +{ + uid_t uid; + time_t time; + const char *res_type, *uuid, *name; + char field[64]; + const char *reason; + int success; + + /* Just skip this record if it failed to get some of the fields */ + if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) + return 0; + + /* Get the resource type */ + auparse_first_record(au); + res_type = auparse_find_field(au, "resrc"); + reason = auparse_find_field(au, "reason"); + if (res_type == NULL) { + if (debug) + fprintf(stderr, "Invalid resrc field.\n"); + return 0; + } + + /* Resource records with these types have old and new values. New + * values indicate resources assignments and are added to the event + * list. Old values are used to update the end time of a resource + * assignment. + */ + int rc = 0; + if (strcmp("disk", res_type) == 0 || + strcmp("vcpu", res_type) == 0 || + strcmp("mem", res_type) == 0 || + strcmp("rng", res_type) == 0 || + strcmp("net", res_type) == 0) { + const char *res = NULL; + /* Resource removed */ + snprintf(field, sizeof(field), "old-%s", res_type); + if(auparse_find_field(au, field)) + res = auparse_interpret_field(au); + if (res == NULL && debug) { + fprintf(stderr, "Failed to get %s field.\n", field); + } else { + rc += update_resource(au, uuid, uid, time, name, + success, reason, res_type, res); + } + + /* Resource added */ + res = NULL; + snprintf(field, sizeof(field), "new-%s", res_type); + if (auparse_find_field(au, field)) + res = auparse_interpret_field(au); + if (res == NULL && debug) { + fprintf(stderr, "Failed to get %s field.\n", field); + } else { + rc += add_resource(au, uuid, uid, time, name, success, + reason, res_type, res); + } + } else if (strcmp("cgroup", res_type) == 0) { + auparse_first_record(au); + const char *cgroup = NULL; + if (auparse_find_field(au, "cgroup")) + cgroup = auparse_interpret_field(au); + rc += add_resource(au, uuid, uid, time, name, success, reason, + res_type, cgroup); + } else if (debug) { + fprintf(stderr, "Found an unknown resource: %s.\n", + res_type); + } + return rc; +} + +/* Search for the last machine_id record with the given seclevel */ +struct event *get_machine_id_by_seclevel(const char *seclevel) +{ + struct event *machine_id = NULL; + list_node_t *it; + + for (it = events->tail; it; it = it->prev) { + struct event *event = it->data; + if (event->type == ET_MACHINE_ID && + event->seclevel != NULL && + strcmp(event->seclevel, seclevel) == 0) { + machine_id = event; + break; + } + } + + return machine_id; +} + +int process_avc_selinux_context(auparse_state_t *au, const char *context) +{ + const char *seclevel; + struct event *machine_id, *avc; + uid_t uid; + time_t time; + + seclevel = get_seclevel(auparse_find_field(au, context)); + if (seclevel == NULL) { + if (debug) { + fprintf(stderr, "Security context not found " + "for AVC event.\n"); + } + return 0; + } + + if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) + return 0; + + machine_id = get_machine_id_by_seclevel(seclevel); + if (machine_id == NULL) { + if (debug) { + fprintf(stderr, "Couldn't get the security " + "level from the AVC event.\n"); + } + return 0; + } + + avc = event_alloc(); + if (avc == NULL) + return 1; + avc->type = ET_AVC; + + /* Guest info */ + avc->uuid = copy_str(machine_id->uuid); + avc->name = copy_str(machine_id->name); + memcpy(avc->proof, machine_id->proof, sizeof(avc->proof)); + + /* AVC info */ + avc->start = time; + avc->uid = uid; + avc->seclevel = copy_str(seclevel); + auparse_first_record(au); + avc->avc_result = copy_str(auparse_find_field(au, "seresult")); + avc->avc_operation = copy_str(auparse_find_field(au, "seperms")); + if (auparse_find_field(au, "comm")) + avc->comm = copy_str(auparse_interpret_field(au)); + if (auparse_find_field(au, "name")) + avc->target = copy_str(auparse_interpret_field(au)); + + /* get the context related to the permission that was denied. */ + if (avc->avc_operation) { + const char *ctx = NULL; + if (strcmp("relabelfrom", avc->avc_operation) == 0) { + ctx = auparse_find_field(au, "scontext"); + } else if (strcmp("relabelto", avc->avc_operation) == 0) { + ctx = auparse_find_field(au, "tcontext"); + } + avc->context = copy_str(ctx); + } + + add_proof(avc, au); + if (list_append(events, avc) == NULL) { + event_free(avc); + return 1; + } + return 0; +} + +/* AVC records are correlated to guest through the selinux context. */ +int process_avc_selinux(auparse_state_t *au) +{ + const char **context; + const char *contexts[] = { "tcontext", "scontext", NULL }; + + for (context = contexts; context && *context; context++) { + if (process_avc_selinux_context(au, *context)) + return 1; + } + return 0; +} + +#ifdef WITH_APPARMOR +int process_avc_apparmor_source(auparse_state_t *au) +{ + uid_t uid = -1; + time_t time = 0; + struct event *avc; + const char *target; + + /* Get the target object. */ + if (auparse_find_field(au, "name") == NULL) { + if (debug) { + auparse_first_record(au); + fprintf(stderr, "Couldn't get the resource name from " + "the AVC record: %s\n", + auparse_get_record_text(au)); + } + return 0; + } + target = auparse_interpret_field(au); + + /* Loop backwards to find a guest session with the target object + * assigned to. */ + struct list_node_t *it; + struct event *res = NULL; + for (it = events->tail; it; it = it->prev) { + struct event *event = it->data; + if (event->success) { + if (event->type == ET_DOWN) { + /* It's just possible to find a matching guest + * session in the current host session. + */ + break; + } else if (event->type == ET_RES && + event->end == 0 && + event->res != NULL && + strcmp(target, event->res) == 0) { + res = event; + break; + } + } + } + + /* Check if a resource event was found. */ + if (res == NULL) { + if (debug) { + fprintf(stderr, "Target object not found for AVC " + "event.\n"); + } + return 0; + } + + if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) + return 0; + + avc = event_alloc(); + if (avc == NULL) + return 1; + avc->type = ET_AVC; + + /* Guest info */ + avc->uuid = copy_str(res->uuid); + avc->name = copy_str(res->name); + memcpy(avc->proof, res->proof, sizeof(avc->proof)); + + /* AVC info */ + avc->start = time; + avc->uid = uid; + auparse_first_record(au); + if (auparse_find_field(au, "apparmor")) { + int i; + avc->avc_result = copy_str(auparse_interpret_field(au)); + for (i = 0; avc->avc_result && avc->avc_result[i]; i++) { + avc->avc_result[i] = tolower(avc->avc_result[i]); + } + } + if (auparse_find_field(au, "operation")) + avc->avc_operation = copy_str(auparse_interpret_field(au)); + avc->target = copy_str(target); + if (auparse_find_field(au, "comm")) + avc->comm = copy_str(auparse_interpret_field(au)); + + add_proof(avc, au); + if (list_append(events, avc) == NULL) { + event_free(avc); + return 1; + } + return 0; +} + +int process_avc_apparmor_target(auparse_state_t *au) +{ + uid_t uid; + time_t time; + const char *profile; + struct event *avc; + + /* Get profile associated with the AVC record */ + if (auparse_find_field(au, "profile") == NULL) { + if (debug) { + auparse_first_record(au); + fprintf(stderr, "AppArmor profile not found for AVC " + "record: %s\n", + auparse_get_record_text(au)); + } + return 0; + } + profile = auparse_interpret_field(au); + + /* Break path to get just the basename */ + const char *basename = profile + strlen(profile); + while (basename != profile && *basename != '/') + basename--; + if (*basename == '/') + basename++; + + /* Check if it is an apparmor profile generated by libvirt and get the + * guest UUID from it */ + const char *prefix = "libvirt-"; + if (strncmp(prefix, basename, strlen(prefix)) != 0) { + if (debug) { + fprintf(stderr, "Found a profile which is not " + "generated by libvirt: %s\n", profile); + } + return 0; + } + + /* Try to find a valid guest session */ + const char *uuid = basename + strlen(prefix); + struct list_node_t *it; + struct event *machine_id = NULL; + for (it = events->tail; it; it = it->prev) { + struct event *event = it->data; + if (event->success) { + if (event->uuid != NULL && + strcmp(event->uuid, uuid) == 0) { + /* machine_id is used here instead of the start + * event because it is generated before any + * other event when a guest is started. So, + * it's possible to correlate AVC events that + * occurs during a guest start. + */ + if (event->type == ET_MACHINE_ID) { + machine_id = event; + break; + } else if (event->type == ET_STOP) { + break; + } + } else if (event->type == ET_DOWN) { + break; + } + } + } + if (machine_id == NULL) { + if (debug) { + fprintf(stderr, "Found an AVC record for an unknown " + "guest.\n"); + } + return 0; + } + + if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) + return 0; + + avc = event_alloc(); + if (avc == NULL) + return 1; + avc->type = ET_AVC; + + /* Guest info */ + avc->uuid = copy_str(machine_id->uuid); + avc->name = copy_str(machine_id->name); + memcpy(avc->proof, machine_id->proof, sizeof(avc->proof)); + + /* AVC info */ + avc->start = time; + avc->uid = uid; + auparse_first_record(au); + if (auparse_find_field(au, "apparmor")) { + int i; + avc->avc_result = copy_str(auparse_interpret_field(au)); + for (i = 0; avc->avc_result && avc->avc_result[i]; i++) { + avc->avc_result[i] = tolower(avc->avc_result[i]); + } + } + if (auparse_find_field(au, "operation")) + avc->avc_operation = copy_str(auparse_interpret_field(au)); + if (auparse_find_field(au, "name")) + avc->target = copy_str(auparse_interpret_field(au)); + if (auparse_find_field(au, "comm")) + avc->comm = copy_str(auparse_interpret_field(au)); + + add_proof(avc, au); + if (list_append(events, avc) == NULL) { + event_free(avc); + return 1; + } + return 0; +} + +/* AVC records are correlated to guest through the apparmor path name. */ +int process_avc_apparmor(auparse_state_t *au) +{ + if (process_avc_apparmor_target(au)) + return 1; + auparse_first_record(au); + return process_avc_apparmor_source(au); +} +#endif + +int process_avc(auparse_state_t *au) +{ + /* Check if it is a SELinux AVC record */ + if (auparse_find_field(au, "tcontext")) { + auparse_first_record(au); + return process_avc_selinux(au); + } + +#ifdef WITH_APPARMOR + /* Check if it is an AppArmor AVC record */ + auparse_first_record(au); + if (auparse_find_field(au, "apparmor")) { + auparse_first_record(au); + return process_avc_apparmor(au); + } +#endif + return 0; +} + +/* This function tries to correlate an anomaly record to a guest using the qemu + * pid or the selinux context. */ +int process_anom(auparse_state_t *au) +{ + uid_t uid; + time_t time; + pid_t pid = -1; + list_node_t *it; + struct event *anom, *start = NULL; + + /* An anomaly record is correlated to a guest by the process id */ + if (auparse_find_field(au, "pid")) { + pid = auparse_get_field_int(au); + } else { + if (debug) { + fprintf(stderr, "Found an anomaly record " + "without pid.\n"); + } + } + + /* Loop backwards to find a running guest with the same pid. */ + if (pid >= 0) { + for (it = events->tail; it; it = it->next) { + struct event *event = it->data; + if (event->pid == pid && event->success) { + if (event->type == ET_STOP) { + break; + } else if (event->type == ET_START) { + if (event->end == 0) + start = event; + break; + } + } + } + } + + /* Try to match using selinux context */ + if (start == NULL) { + const char *seclevel; + struct event *machine_id; + + seclevel = get_seclevel(auparse_find_field(au, "subj")); + if (seclevel == NULL) { + if (debug) { + auparse_first_record(au); + const char *text = auparse_get_record_text(au); + fprintf(stderr, "Security context not found " + "for anomaly event: %s\n", + text ? text : ""); + } + return 0; + } + machine_id = get_machine_id_by_seclevel(seclevel); + if (machine_id == NULL) { + if (debug) { + fprintf(stderr, "Couldn't get the security " + "level from the anomaly event.\n"); + } + return 0; + } + + for (it = events->tail; it; it = it->next) { + struct event *event = it->data; + if (event->success && machine_id->uuid && event->uuid && + strcmp(machine_id->uuid, event->uuid) == 0) { + if (event->type == ET_STOP) { + break; + } else if (event->type == ET_START) { + if (event->end == 0) + start = event; + break; + } + } + } + } + + if (start == NULL) { + if (debug) { + const char *text = auparse_get_record_text(au); + fprintf(stderr, "Guest not found for " + "anomaly record: %s.\n", + text ? text : ""); + } + return 0; + } + + if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) + return 0; + + anom = event_alloc(); + if (anom == NULL) + return 1; + anom->type = ET_ANOM; + anom->uuid = copy_str(start->uuid); + anom->name = copy_str(start->name); + anom->uid = uid; + anom->start = time; + anom->pid = pid; + memcpy(anom->proof, start->proof, sizeof(anom->proof)); + add_proof(anom, au); + if (list_append(events, anom) == NULL) { + event_free(anom); + return 1; + } + return 0; +} + +int process_shutdown(auparse_state_t *au) +{ + uid_t uid = -1; + time_t time = 0; + struct event *down; + list_node_t *it; + int success = 0; + + if (extract_virt_fields(au, NULL, &uid, &time, NULL, &success)) + return 0; + + for (it = events->tail; it; it = it->prev) { + struct event *event = it->data; + if (event->success) { + if (event->type == ET_START || event->type == ET_RES) { + if (event->end == 0) { + event->end = time; + add_proof(event, au); + } + } else if (event->type == ET_DOWN) { + break; + } + } + } + + down = event_alloc(); + if (down == NULL) + return 1; + down->type = ET_DOWN; + down->uid = uid; + down->start = time; + down->success = success; + add_proof(down, au); + if (list_append(events, down) == NULL) { + event_free(down); + return 1; + } + return 0; +} + +/* Convert record type to a string */ +const char *get_rec_type(struct event *e) +{ + static char buf[64]; + if (e == NULL) + return ""; + + switch (e->type) { + case ET_START: + return "start"; + case ET_STOP: + return "stop"; + case ET_RES: + return "res"; + case ET_AVC: + return "avc"; + case ET_ANOM: + return "anom"; + case ET_DOWN: + return "down"; + } + + snprintf(buf, sizeof(buf), "%d", e->type); + return buf; +} + +/* Convert uid to a string */ +const char *get_username(struct event *e) +{ + static char s[256]; + if (!e || (int)e->uid == -1) { + s[0] = '?'; + s[1] = '\0'; + } else { + struct passwd *passwd = getpwuid(e->uid); + if (passwd == NULL || passwd->pw_name == NULL) { + snprintf(s, sizeof(s), "%d", e->uid); + } else { + snprintf(s, sizeof(s), "%s", passwd->pw_name); + } + } + return s; +} + +/* Convert a time period to string */ +const char *get_time_period(struct event *event) +{ + size_t i = 0; + static char buf[128]; + + i += sprintf(buf + i, "%-16.16s", ctime(&event->start)); + if (event->end) { + time_t secs = event->end - event->start; + int mins, hours, days; + i += sprintf(buf + i, " - %-7.5s", ctime(&event->end) + 11); + mins = (secs / 60) % 60; + hours = (secs / 3600) % 24; + days = secs / 86400; + if (days) { + i += sprintf(buf + i, "(%d+%02d:%02d)", days, hours, + mins); + } else { + i += sprintf(buf + i, "(%02d:%02d)", hours, mins); + } + } else { + if (!event->success && + event->type != ET_AVC && + event->type != ET_ANOM) { + i += sprintf(buf + i, " - failed"); + } + } + return buf; +} + +void print_event(struct event *event) +{ + /* Auxiliary macro to convert NULL to "" */ + #define N(str) ((str) ? str : "") + + /* machine id records are used just to get information about + * the guests. */ + if (event->type == ET_MACHINE_ID) + return; + /* If "--all-events" is not given, only the start event is shown. */ + if (!all_events_flag && event->type != ET_START) + return; + /* The type of event is shown only when all records are shown */ + if (all_events_flag) + printf("%-5.5s ", get_rec_type(event)); + + /* Print common fields */ + printf("%-25.25s", N(event->name)); + if (uuid_flag) + printf("\t%-36.36s", N(event->uuid)); + printf("\t%-11.11s\t%-35.35s", get_username(event), + get_time_period(event)); + + /* Print type specific fields */ + if (event->type == ET_RES) { + printf("\t%-12.12s", N(event->res_type)); + printf("\t%-10.10s", N(event->reason)); + if (strcmp("cgroup", event->res_type) != 0) { + printf("\t%s", N(event->res)); + } else { + printf("\t%s\t%s\t%s", N(event->cgroup_class), + N(event->cgroup_acl), + N(event->cgroup_detail)); + } + } else if (event->type == ET_MACHINE_ID) { + printf("\t%s", N(event->seclevel)); + } else if (event->type == ET_AVC) { + printf("\t%-12.12s", N(event->avc_operation)); + printf("\t%-10.10s", N(event->avc_result)); + printf("\t%s\t%s\t%s", N(event->comm), N(event->target), + N(event->context)); + } + printf("\n"); + + /* Print proof */ + if (proof_flag) { + int first = 1; + int i, len = sizeof(event->proof)/sizeof(event->proof[0]); + printf(" Proof:"); + for (i = 0; i < len; i++) { + if (event->proof[i].time) { + printf("%s %ld.%03u:%lu", + (first) ? "" : ",", + event->proof[i].time, + event->proof[i].milli, + event->proof[i].serial); + first = 0; + } + } + printf("\n\n"); + } +} + +/* Print all events */ +void print_events(void) +{ + list_node_t *it; + for (it = events->head; it; it = it->next) { + struct event *event = it->data; + if (event) + print_event(event); + } +} + +/* Count and print summary */ +void print_summary(void) +{ + /* Summary numbers */ + time_t start_time = 0, end_time = 0; + long start = 0, stop = 0, res = 0, avc = 0, anom = 0, + shutdown = 0, failure = 0; + char start_buf[32], end_buf[32]; + + /* Calculate summary */ + list_node_t *it; + for (it = events->head; it; it = it->next) { + struct event *event = it->data; + if (event->success == 0 && + (event->type == ET_START || + event->type == ET_STOP || + event->type == ET_RES)) { + failure++; + } else { + switch (event->type) { + case ET_START: + start++; + break; + case ET_STOP: + stop++; + break; + case ET_RES: + res++; + break; + case ET_AVC: + avc++; + break; + case ET_ANOM: + anom++; + break; + case ET_DOWN: + shutdown++; + break; + } + } + + /* Calculate time range */ + if (event->start) { + if (start_time == 0 || event->start < start_time) { + start_time = event->start; + } + if (end_time == 0 || event->start > end_time) { + end_time = event->start; + } + } + if (event->end) { + if (start_time == 0 || event->end < start_time) { + start_time = event->end; + } + if (end_time == 0 || event->end > end_time) { + end_time = event->end; + } + } + + } + + if (start_time) + ctime_r(&start_time, start_buf); + else + strcpy(start_buf, "undef"); + if (end_time) + ctime_r(&end_time, end_buf); + else + strcpy(end_buf, "undef"); + + /* Print summary */ + printf("Range of time for report: %-.16s - %-.16s\n", + start_buf, end_buf); + printf("Number of guest starts: %ld\n", start); + printf("Number of guest stops: %ld\n", stop); + printf("Number of resource assignments: %ld\n", res); + printf("Number of related AVCs: %ld\n", avc); + printf("Number of related anomalies: %ld\n", anom); + printf("Number of host shutdowns: %ld\n", shutdown); + printf("Number of failed operations: %ld\n", failure); +} + +int main(int argc, char **argv) +{ + int rc = 0; + auparse_state_t *au = NULL; + + setlocale(LC_ALL, ""); + if (parse_args(argc, argv)) + goto error; + if (help_flag) { + usage(stdout); + goto exit; + } + + /* Initialize event list*/ + events = list_new((list_free_data_fn*) event_free); + if (events == NULL) + goto unexpected_error; + + /* Initialize auparse */ + au = init_auparse(); + if (au == NULL) + goto error; + if (create_search_criteria(au)) + goto error; + + while (ausearch_next_event(au) > 0) { + int err = 0; + + switch(auparse_get_type(au)) { + case AUDIT_VIRT_MACHINE_ID: + err = process_machine_id_event(au); + break; + case AUDIT_VIRT_CONTROL: + err = process_control_event(au); + break; + case AUDIT_VIRT_RESOURCE: + err = process_resource_event(au); + break; + case AUDIT_AVC: + err = process_avc(au); + break; + case AUDIT_FIRST_ANOM_MSG ... AUDIT_LAST_ANOM_MSG: + case AUDIT_FIRST_KERN_ANOM_MSG ... AUDIT_LAST_KERN_ANOM_MSG: + err = process_anom(au); + break; + case AUDIT_SYSTEM_SHUTDOWN: + err = process_shutdown(au); + break; + } + if (err) { + goto unexpected_error; + } + auparse_next_event(au); + } + + /* Show results */ + if (summary_flag) { + print_summary(); + } else { + print_events(); + } + + /* success */ + goto exit; + +unexpected_error: + fprintf(stderr, "Unexpected error\n"); +error: + rc = 1; +exit: + if (au) + auparse_destroy(au); + list_free(events); + if (debug) + fprintf(stdout, "Exit code: %d\n", rc); + return rc; +} + -- cgit 1.2.3-korg