Page name: AppArmorLoggingHack

AppArmor Logging Hack

Credits: unknown

Anleitung für AppArmor Umfassende AppArmor Policy

Beim Versuch, diese Policy einzuspielen fing AppArmor an, wie wahnsinnig zu loggen.

Eine generelle Methode, nur Denials von AppArmor zu erhalten scheint nicht zu existieren, vgl. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/security/apparmor/audit.c#n164

Den Kernel jedes Mal neu zu kompilieren mit meinem Patch war mir zu blöd, und Upstream scheint das Problem zwar bekannt, aber keiner dazu bereit sich zu rühren. Also dem Kernel mit eBPF, Livepatch oder KProbes zuleibe rücken. Für die Zieldistro blieb nur mehr KProbes.

Makefile

SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
#MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
.DEFAULT: all

obj-m   += logfilter.o

PWD     := $(CURDIR)

.PHONY: all
all: ## Builds kernel module
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

.PHONY: help
help: # see https://diamantidis.github.io/tips/2020/07/01/list-makefile-targets
        @grep -E '^[\a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
        | sed -n 's/^\(.*\): \(.*\)##\(.*\)/\1|\3/p' \
        | column -t  -s '|'

.PHONY: clean
clean: ## cleans up build dir
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

# https://www.xmodulo.com/build-kernel-module-dkms-linux.html
.PHONY: dkms_install
dkms_install: ## Install as DKMS module, build, and install
        sudo -s << EOF
        cp -R . /usr/src/logfilter-1.0.0
        cd /usr/src/logfilter-1.0.0
        dkms add logfilter/1.0.0
        dkms build logfilter/1.0.0
        dkms install logfilter/1.0.0
        dkms status | grep logfilter
        EOF

logfilter.c

/* vim:set ts=8 sts=8 sw=8 tw=80 cc=80 noet: */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/audit.h>
#include <linux/lsm_audit.h>
#include <linux/kprobes.h>

/* TODO: extract this from security/apparmor/include/audit.h */
enum audit_type {
        AUDIT_APPARMOR_AUDIT,
        AUDIT_APPARMOR_ALLOWED,
        AUDIT_APPARMOR_DENIED,
        AUDIT_APPARMOR_HINT,
        AUDIT_APPARMOR_STATUS,
        AUDIT_APPARMOR_ERROR,
        AUDIT_APPARMOR_KILL,
        AUDIT_APPARMOR_AUTO
};

struct apparmor_audit_data {
        int error;
        int type;
        u16 class;
        const char *op;
        struct aa_label *label;
        const char *name;
        const char *info;
        u32 request;
        u32 denied;
        union {
                /* these entries require a custom callback fn */
                struct {
                        struct aa_label *peer;
                        union {
                                struct {
                                        const char *target;
                                        kuid_t ouid;
                                } fs;
                                struct {
                                        int rlim;
                                        unsigned long max;
                                } rlim;
                                struct {
                                        int signal;
                                        int unmappedsig;
                                };
                                struct {
                                        int type, protocol;
                                        struct sock *peer_sk;
                                        void *addr;
                                        int addrlen;
                                } net;
                        };
                };
                struct {
                        struct aa_profile *profile;
                        const char *ns;
                        long pos;
                } iface;
                struct {
                        const char *src_name;
                        const char *type;
                        const char *trans;
                        const char *data;
                        unsigned long flags;
                } mnt;
        };
};

#define aad(SA) ((SA)->apparmor_audit_data)
/* END TODO */

#define KPROBE_PRE_HANDLER(fname) static int __kprobes fname(struct kprobe *p, struct pt_regs *regs)

KPROBE_PRE_HANDLER(patch)
{
        /*
         * x86, 64bit
         * Most of the kernel functions (including those with variable argument
         * list) get the first 6 arguments in rdi, rsi, rdx, rcx, r8, r9, in 
         * that order, the remaining ones go on stack. *(esp+8) should be the 
         * 7th argument, *(esp+16) - the 8th and so on.
         *
         * https://stackoverflow.com/questions/10563635/getting-function-arguments-using-kprobes
         */
        unsigned long rdi = regs->di;
        struct common_audit_data *a = (struct common_audit_data*) rdi;

        /* is this an AppArmor event? */
        if (aad(a)) {
                /* get event type */
                int type = aad(a)->type;

                /* filter if we are not interested */
                if (type != AUDIT_APPARMOR_KILL && 
                    type != AUDIT_APPARMOR_DENIED &&
                    type != AUDIT_APPARMOR_STATUS) {
                        /* set common_audit_data = NULL, then common_lsm_audit
                         * returns immediately */
                        regs->di = 0;
                }
        }

        return 0;
}

static struct kprobe kp_patch = {
        .symbol_name = "common_lsm_audit",
        .pre_handler = patch
};

static __init int logfilter_init(void)
{
        int result = register_kprobe(&kp_patch);

        if(result) {
                printk(KERN_INFO "Failed to register kprobe\n");
                return result;
        } else {
                printk(KERN_INFO "Registered common_lsm_audit patch\n");
        }

        return 0;
}

static __exit void logfilter_exit(void)
{
        unregister_kprobe(&kp_patch);
        printk(KERN_INFO "Unregistered common_lsm_audit patch\n");
}

module_init(logfilter_init);
module_exit(logfilter_exit);

MODULE_DESCRIPTION("auditlog filtering");
MODULE_AUTHOR("unknown, fuero");
MODULE_LICENSE("GPL");

dkms.conf

PACKAGE_NAME="logfilter"
PACKAGE_VERSION="1.0.0"
BUILT_MODULE_NAME[0]="logfilter"
DEST_MODULE_LOCATION[0]="/kernel/extra/"
AUTOINSTALL="yes"