Skip to content
Snippets Groups Projects
Commit 061cc818 authored by Martyn Welch's avatar Martyn Welch
Browse files

Replace aa_log_extract_tokens.pl


The script aa_log_extract_tokens.pl expects the format of the logs from
audit. As we don't have audit in the majority of our images and would like
to extract this information from the journal, remove this script and
replace with a shell function.

Signed-off-by: default avatarMartyn Welch <martyn.welch@collabora.co.uk>
parent a4d53f36
No related branches found
Tags apertis/0.2021.5
No related merge requests found
#!/usr/bin/env perl
# vim: set sts=4 sw=4 et tw=80 :
#
# Copyright (c) 2006 Novell, Inc. All Rights Reserved.
# Copyright (c) 2010 Canonical, Ltd.
# Copyright (c) 2012-2015 Collabora Ltd.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# 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, contact Novell, Inc.
#
# To contact Novell about this file by physical or electronic mail,
# you may find current contact information at www.novell.com.
# Example script to extract tokens from apparmor events in audit.log
# We use auditd because otherwise messages are lost if they come too quickly
# http://wiki.apparmor.net/index.php/AppArmorMonitoring#Realtime_information
#
#
# Example complaint event:
# type=AVC msg=audit(1344659645.579:153): apparmor="ALLOWED" operation="open"
# parent=15215 profile="/home/chaiwala/bin/*" name="/etc/group" pid=15216
# comm="ls" requested_mask="r" denied_mask="r" fsuid=0 ouid=0
#
# Example audit event:
# type=AVC msg=audit(1344735566.384:827): apparmor="AUDIT" operation="getattr"
# parent=16769 profile="/home/chaiwala/bin/*" name="/etc/localtime" pid=16770
# comm="ls" requested_mask="r" fsuid=1000 ouid=0
#
use strict;
use warnings;
use LibAppArmor qw();
use constant DEBUG => $ENV{AA_LOG_DEBUG};
my $event;
my $record;
my $value;
my $profile_wanted;
my @event_modes;
my @skip_names;
my %records_order;
sub usage {
print("Usage: $0 <event mode> [event mode]...\n");
print("And pipe logs to it\n");
print("Valid values for [event mode] are: PERMITTING, REJECTING, AUDITING\n");
}
# Ensure there are some arguments
if ($#ARGV < 0) {
usage();
exit(1);
}
# Ensure that the arguments are all valid
foreach (@ARGV) {
SWITCH: {
if (/PERMITTING/) { last SWITCH; }
if (/REJECTING/) { last SWITCH; }
if (/AUDITING/) { last SWITCH; }
usage();
exit(1);
}
}
# Valid values we'll use: PERMITTING, REJECTING, AUDITING
# Valid values that we're not interested in: STATUS, ERROR, UNKNOWN, HINT
# PERMITTING is emitted when:
# * AppArmor is in "normal" audit mode[1]
# * A profile loaded in complain mode matches something
# REJECTING is emitted when:
# * AppArmor is in "normal" audit mode[1]
# * A profile loaded in enforce mode matches something
# AUDITING is emitted when:
# * AppArmor is in "all" audit mode[1]
#
# 1. http://wiki.apparmor.net/index.php/AppArmor_Failures#Module_audit_settings
@event_modes = @ARGV;
# We're being lazy and getting arguments from the environment instead of doing
# getopt parsing
$profile_wanted = $ENV{'AA_PROFILE_WANTED'};
# Regex of file paths to skip accesses to
@skip_names=(
"/etc/ld\.so\.cache",
"/lib/i386-linux-gnu/libc.*\.so",
"/dev/pts/.*",
"/dev/(null|zero|random|urandom)",
);
# Arbitrary order, but we want to keep it constant.
# chaiwala-apparmor-tests rely on the following order.
%records_order = (
'profile' => 1,
'sdmode' => 2,
'denied_mask' => 3,
'operation' => 4,
'name' => 5,
'request_mask' => 6,
);
sub records_sort {
if (exists $records_order{$a} && exists $records_order{$b})
{
return ($records_order{$a}) <=> ($records_order{$b});
}
if (! exists $records_order{$a} && !exists $records_order{$b})
{
return $a cmp $b;
}
return exists $records_order{$a} ? -1 : 1;
}
# Simplified from Immunix::AppArmor.
sub stringify_sdmode {
my $sdmode = shift;
return 'ERROR' if $sdmode == 1;
return 'AUDITING' if $sdmode == 2;
return 'PERMITTING' if $sdmode == 3;
return 'REJECTING' if $sdmode == 4;
return 'HINT' if $sdmode == 5;
return 'STATUS' if $sdmode == 6;
# no idea - output *something*
return $sdmode;
}
# Simplified from Immunix::AppArmor. We're not using its parse_event
# because that only works properly for events whose mask is something
# like r or w, not for events whose mask is more like receive or send;
# this version is more "raw", and hopefully a little more robust against
# AA changes.
sub parse_event {
my %ev = ();
my $msg = shift;
chomp($msg);
my $event = LibAppArmor::parse_record($msg);
$ev{resource} = LibAppArmor::aa_log_record::swig_info_get($event);
$ev{active_hat} = LibAppArmor::aa_log_record::swig_active_hat_get($event);
$ev{sdmode} = LibAppArmor::aa_log_record::swig_event_get($event);
$ev{time} = LibAppArmor::aa_log_record::swig_epoch_get($event);
$ev{operation} = LibAppArmor::aa_log_record::swig_operation_get($event);
$ev{profile} = LibAppArmor::aa_log_record::swig_profile_get($event);
$ev{name} = LibAppArmor::aa_log_record::swig_name_get($event);
$ev{name2} = LibAppArmor::aa_log_record::swig_name2_get($event);
$ev{attr} = LibAppArmor::aa_log_record::swig_attribute_get($event);
$ev{parent} = LibAppArmor::aa_log_record::swig_parent_get($event);
$ev{pid} = LibAppArmor::aa_log_record::swig_pid_get($event);
$ev{task} = LibAppArmor::aa_log_record::swig_task_get($event);
$ev{info} = LibAppArmor::aa_log_record::swig_info_get($event);
$ev{denied_mask} = LibAppArmor::aa_log_record::swig_denied_mask_get($event);
$ev{request_mask} = LibAppArmor::aa_log_record::swig_requested_mask_get($event);
$ev{magic_token} = LibAppArmor::aa_log_record::swig_magic_token_get($event);
$ev{family} = LibAppArmor::aa_log_record::swig_net_family_get($event);
$ev{protocol} = LibAppArmor::aa_log_record::swig_net_protocol_get($event);
$ev{sock_type} = LibAppArmor::aa_log_record::swig_net_sock_type_get($event);
LibAppArmor::free_record($event);
if (! $ev{sdmode}) {
return undef;
}
$ev{sdmode} = stringify_sdmode($ev{sdmode});
if (DEBUG) {
print STDERR "/----\n";
foreach my $k (sort keys %ev) {
if (defined $ev{$k}) {
print STDERR "\t", $k, "\t", $ev{$k}, "\n";
}
}
print STDERR "\\----\n";
}
return \%ev;
}
EVENT: while (<STDIN>) {
# This doesn't give us all the tokens, unfortunately.
# Only gives us: profile, sdmode, time, denied_mask, pid, operation,
# parent (parent pid), name (filename), request_mask
# (FIXME: it could now give more if we want them)
$event = parse_event($_);
if (!$event ||
!(grep { $_ eq $$event{'sdmode'} } @event_modes) ||
($profile_wanted && ($$event{'profile'} ne $profile_wanted)))
{
next EVENT;
}
foreach (@skip_names) {
if (defined $$event{'name'} && $$event{'name'} =~ /$_/) {
next EVENT;
}
}
print "====\n";
# This is pretty much it. Go forth and use this hash in whatever way.
foreach $record (sort records_sort keys %$event) {
$value = $$event{$record};
next unless $value;
# Skip records that keep changing with every invocation
SWITCH: {
if ($record eq "time") { last SWITCH; }
if ($record eq "pid") { last SWITCH; }
if ($record eq "parent") { last SWITCH; }
print "$record:$value\n";
}
}
}
# Need to source this file.
# Pass in audit string, returns formatted block as `aa_log_extract_tokens.pl`
# would have provided. Need to return:
#
# profile:/usr/bin/busctl
# sdmode:REJECTING
# denied_mask:r
# operation:open
# name:/proc/879/stat
# request_mask:r
#
# Note: apparmor uses the term "DENIED" rather than "REJECTING", but our
# expected values want "REJECTING", so tweak for now.
apparmor_parse_journal() {
awk -v event=$2 '
{
for (i = 1; i <= NF; i++) {
n = index($i, "=");
if(n) {
vars[substr($i, 1, n - 1)] = substr($i, n + 1)
# Strip quotes
gsub("\"","",vars[substr($i, 1, n - 1)])
}
}
}
{
if (vars["apparmor"] == event) {
print "===="
print "profile:"vars["profile"]
# Match old nomenclature
if (vars["apparmor"] == "DENIED") {
vars["apparmor"] = "REJECTING"
}
print "sdmode:"vars["apparmor"]
print "denied_mask:"vars["denied_mask"]
print "operation:"vars["operation"]
print "name:"vars["name"]
print "request_mask:"vars["requested_mask"]
}
}
' $1
}
......@@ -8,6 +8,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
. common/common-apparmor.sh
ALTERNATIVE_SEPARATOR="## alternative ##"
END=2
......@@ -52,7 +54,6 @@ if [ ! -x $1 ]; then
exit 1
fi
SOMETHING_FAILED="False"
# typically "normal.expected" or "malicious.expected"
TEST_TITLE=$( basename ${EXPECT_FILE} )
......@@ -112,7 +113,8 @@ else
echo "# ${CMDLINE} exited ${RET}"
# typically "normal.expected_underlying_tests: fail"
echo "${TEST_TITLE}_underlying_tests: fail"
SOMETHING_FAILED="True"
exit 1
fi
# Give journal time to log the entries.
......@@ -124,11 +126,11 @@ journalctl -S "${START_TIME}" -t audit -o cat > ${AUDIT_FILE}
echo "#=== ${TEST_TITLE} ==="
echo "#---8<--- raw output in audit log"
echo "#---8<--- raw apparmor output from journal"
cat ${AUDIT_FILE} | sed 's/^/# /'
echo "#--->8---"
echo "#---8<--- expected output from aa_log_extract_tokens.pl"
echo "#---8<--- expected parsed apparmor output from journal"
cat ${EXPECT_FILE} | sed 's/^/# /'
echo "#--->8---"
......@@ -141,29 +143,12 @@ done
PARSE_FILE="${TMP_DIR}/PARSE"
RET=$( cat ${AUDIT_FILE} | common/aa_log_extract_tokens.pl REJECTING > ${PARSE_FILE} )
if [ "${RET}" != "0" ]; then
echo "# aa_log_extract_tokens.pl failed, trying line-by-line..."
LINES=$(wc -l ${AUDIT_FILE} | cut -d ' ' -f1 )
cat ${AUDIT_FILE} | while read LINE; do
echo ${LINE} | common/aa_log_extract_tokens.pl REJECTING 2>${TMP_DIR}/STDERR > ${TMP_DIR}/STDOUT
RET=$?
cat ${TMP_DIR}/STDOUT >> ${TMP_DIR}/ERRPARSE
cat ${TMP_DIR}/STDERR | sed 's/^/E: /' >> ${TMP_DIR}/ERRPARSE
# TODO: There is potential for other processes to cause messages to appear in
# the journal that may lead to false failures. If this is found to be an
# issue in practice, then additional filtering of results may be required
apparmor_parse_journal ${AUDIT_FILE} DENIED > ${PARSE_FILE}
if [ "$RET" != "0" ]; then
echo -n "^ original line: ${LINE}" >> ${TMP_DIR}/ERRPARSE
fi
done
mv ${TMP_DIR}/ERRPARSE ${PARSE_FILE}
fi
echo "#---8<--- actual output from aa_log_extract_tokens.pl"
echo "#---8<--- actual parsed apparmor output from journal"
cat ${PARSE_FILE} | sed 's/^/# /'
echo "#--->8---"
......@@ -189,10 +174,6 @@ else
diff -urN ${TMP_DIR}/EXPECT${NUM} ${PARSE_FILE}
echo "#--->8---"
echo "${TEST_TITLE}: fail"
SOMETHING_FAILED="True"
fi
if [ "${SOMETHING_FAILED}" = "True" ]; then
exit 1
fi
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment