apparmor: add package
authorOskari Rauta <redacted>
Tue, 18 May 2021 01:27:21 +0000 (04:27 +0300)
committerPolynomdivision <redacted>
Thu, 10 Jun 2021 16:11:57 +0000 (18:11 +0200)
Signed-off-by: Oskari Rauta <redacted>
utils/apparmor/Makefile [new file with mode: 0644]
utils/apparmor/files/apparmor.init [new file with mode: 0755]
utils/apparmor/files/apparmor.sh [new file with mode: 0755]
utils/apparmor/patches/010-autoconf-libapparmor.patch [new file with mode: 0644]
utils/apparmor/patches/020-fix-ss-path.patch [new file with mode: 0644]
utils/apparmor/patches/030-remove-pynotify2-dep.patch [new file with mode: 0644]
utils/apparmor/patches/040-remove-bash-dep.patch [new file with mode: 0644]
utils/apparmor/patches/050-disable-man-pages.patch [new file with mode: 0644]
utils/apparmor/patches/060-openwrt-dnsmasq-profile.patch [new file with mode: 0644]

diff --git a/utils/apparmor/Makefile b/utils/apparmor/Makefile
new file mode 100644 (file)
index 0000000..c72ea68
--- /dev/null
@@ -0,0 +1,216 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=apparmor
+PKG_VERSION:=3.0.1
+PKG_RELEASE:=$(AUTORELEASE)
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL:=https://gitlab.com/apparmor/apparmor.git
+PKG_SOURCE_VERSION:=0325ba06da6eeb5acf3e568063a08136fd0913e0
+PKG_MIRROR_HASH:=303ceca041ad8023fa44cdda366448d60b6299790266834b4078d30b70ad27f9
+
+PKG_LICENSE:=GPL-2.0-only
+PKG_LICENSE_FILES:=LICENSE
+PKG_MAINTAINER:=Oskari Rauta <oskari.rauta@gmail.com>
+BUILDONLY:=1
+
+PKG_BUILD_DEPENDS:=swig/host
+HOST_PYTHON3_PACKAGE_BUILD_DEPENDS:=setuptools-scm
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/nls.mk
+include ../../../packages/lang/python/python3-package.mk
+
+define Package/apparmor/Default
+  SECTION:=utils
+  CATEGORY:=Utilities
+  SUBMENU:=AppArmor
+  URL:=https://apparmor.net
+endef
+
+define Package/libapparmor
+  TITLE:=AppArmor library
+  SECTION:=libs
+  CATEGORY:=Libraries
+  URL:=https://apparmor.net
+endef
+
+define Package/python3-apparmor
+  TITLE:=AppArmor Python bindings
+  SECTION:=lang
+  CATEGORY:=Languages
+  SUBMENU:=Python
+  URL:=https://apparmor.net
+  DEPENDS:=+libapparmor +python3
+endef
+
+define Package/apparmor-utils
+  $(call Package/apparmor/Default)
+  TITLE:=AppArmor utils
+  DEPENDS:=$(INTL_DEPENDS) +libapparmor +python3-apparmor +python3 +python3-readline +python3-psutil +ss +findutils-xargs
+endef
+
+define Package/apparmor-profiles
+  $(call Package/apparmor/Default)
+  TITLE:=AppArmor default profiles
+endef
+
+define Package/libapparmor/description
+  Library to support AppArmor userspace utilities
+endef
+
+define Package/apparmor-utils/description
+  AppArmor application security system init script and
+  userspace utils to assist with AppArmor profile management
+endef
+
+define Package/apparmor-profiles/description
+  A collection of profiles for the AppArmor application security system
+endef
+
+CONFIGURE_PATH=libraries/libapparmor
+
+TARGET_LDFLAGS += \
+               -L$(PYTHON3_LIB_DIR)
+
+CONFIGURE_VARS += \
+               SHELL=$(bash) \
+               PYTHON_VERSION=$(PYTHON3_VERSION) \
+               PYTHON_VERSIONS=$(PYTHON3) \
+               PYTHON=$(PYTHON3) \
+               PYTHON_CONFIG=$(STAGING_DIR_ROOT)/host/bin/python$(PYTHON3_VERSION)-config \
+               PYTHON_LDFLAGS="-L$(STAGING_DIR)/usr/lib -L$(PYTHON3_LIB_DIR)" \
+               PYTHON_CPPFLAGS="-I$(STAGING_DIR)/usr/include/python$(PYTHON3_VERSION)" \
+               PYTHON_LDFLAGS="-I$(PYTHON3_INC_DIR) -L$(STAGING_DIR)/usr/lib -L$(PYTHON3_LIB_DIR)" \
+               PYTHON_EXTRA_LDFLAGS="-L$(STAGING_DIR)/usr/lib -L$(PYTHON3_LIB_DIR)/config-$(PYTHON3_VERSION) -lpthread -ldl -lm -lz -lpython$(PYTHON3_VERSION)" \
+               ac_cv_path_PYTHON_CONFIG="$(STAGING_DIR)/host/bin/python$(PYTHON3_VERSION)-config"
+
+CONFIGURE_ARGS += \
+               --with-python \
+               --without-perl \
+               --without-ruby \
+               --disable-man-pages
+
+ifeq ($(CONFIG_BUILD_NLS),y)
+       MAKE_VARS += WITH_LIBINTL=1
+       MAKE_FLAGS += WITH_LIBINTL=1
+endif
+
+APPARMOR_CFLAGS = -I$(PKG_BUILD_DIR)/libraries/libapparmor/include
+APPARMOR_LDFLAGS = -L$(PKG_BUILD_DIR)/libraries/libapparmor/src/.libs
+
+define Build/Configure
+       $(MAKE) -C $(PKG_BUILD_DIR)/libraries/libapparmor configure
+       $(RM) $(PKG_BUILD_DIR)/libraries/libapparmor/Makefile
+       $(SED) 's#ac_cv_path_PYTHON_CONFIG=#ac_cv_path_X_PYTHON_CONFIG=#g' $(PKG_BUILD_DIR)/libraries/libapparmor/configure
+       $(call Build/Configure/Default)
+endef
+
+define Build/Compile
+       # Building libapparmor
+       +$(MAKE_VARS) \
+               CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/libraries/libapparmor \
+               $(MAKE_FLAGS)
+       # Building parser
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) \
+               CFLAGS="$(TARGET_CFLAGS) $(APPARMOR_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS) $(APPARMOR_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS) $(APPARMOR_LDFLAGS) -lgcc_s" USE_SYSTEM=0 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/parser \
+               $(MAKE_FLAGS) apparmor_parser
+       # Building binutils
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) \
+               CFLAGS="$(TARGET_CFLAGS) $(APPARMOR_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS) $(APPARMOR_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS) $(APPARMOR_LDFLAGS)" USE_SYSTEM=0 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/binutils \
+               $(MAKE_FLAGS)
+       # Building utils
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) \
+               CFLAGS="$(TARGET_CFLAGS) $(APPARMOR_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS) $(APPARMOR_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS) $(APPARMOR_LDFLAGS)" USE_SYSTEM=0 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/utils \
+               $(MAKE_FLAGS)
+       # Building profiles
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) \
+               CFLAGS="$(TARGET_CFLAGS) $(APPARMOR_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS) $(APPARMOR_CFLAGS")" LDFLAGS="$(TARGET_LDFLAGS) $(APPARMOR_LDFLAGS)" USE_SYSTEM=0 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/profiles \
+               $(MAKE_FLAGS)
+endef
+
+define Build/Install
+       # Make sure we have python's setup tools installed
+       $(if $(PYTHON3_PKG_HOST_PIP_INSTALL_ARGS), \
+               $(call HostPython3/PipInstall,$(PYTHON3_PKG_HOST_PIP_INSTALL_ARGS)) \
+       )
+       $(INSTALL_DIR) $(PKG_INSTALL_DIR)-libapparmor $(PKG_INSTALL_DIR)-utils $(PKG_INSTALL_DIR)-profiles
+       # Installing libapparmor
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) VERSION=$(PYTHON3_VERSION) \
+               CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" \
+               $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/libraries/libapparmor \
+               $(MAKE_FLAGS) DESTDIR="$(PKG_INSTALL_DIR)-libapparmor" install
+       # Installing parser
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) VERSION=$(PYTHON3_VERSION) \
+               CFLAGS="$(TARGET_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" USE_SYSTEM=1 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/parser \
+               $(MAKE_FLAGS) DESTDIR="$(PKG_INSTALL_DIR)-utils" install
+       # Installing binutils
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) VERSION=$(PYTHON3_VERSION) \
+               CFLAGS="$(TARGET_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" USE_SYSTEM=1 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/binutils \
+               $(MAKE_FLAGS) DESTDIR="$(PKG_INSTALL_DIR)-utils" install
+       # Installing utils
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) VERSION=$(PYTHON3_VERSION) \
+               CFLAGS="$(TARGET_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" USE_SYSTEM=1 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/utils \
+               $(MAKE_FLAGS) DESTDIR="$(PKG_INSTALL_DIR)-utils" install
+       # Installing profiles
+       +$(MAKE_VARS) PYTHON=$(HOST_PYTHON) VERSION=$(PYTHON3_VERSION) \
+               CFLAGS="$(TARGET_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" USE_SYSTEM=1 $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/profiles \
+               $(MAKE_FLAGS) DESTDIR="$(PKG_INSTALL_DIR)-profiles" install
+endef
+
+define Package/libapparmor/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)-libapparmor/usr/lib/libapparmor.so.1 $(1)/usr/lib/
+       $(LN) libapparmor.so.1 $(1)/usr/lib/libapparmor.so
+endef
+
+define Package/python3-apparmor/install
+       $(INSTALL_DIR) \
+               $(1)/usr/lib/python$(PYTHON3_VERSION)/site-packages \
+               $(1)/usr/lib/python$(PYTHON3_VERSION)/site-packages/LibAppArmor
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-libapparmor/usr/lib/python$(PYTHON3_VERSION)/site-packages/*.egg-info \
+               $(1)/usr/lib/python$(PYTHON3_VERSION)/site-packages/
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-libapparmor/usr/lib/python$(PYTHON3_VERSION)/site-packages/LibAppArmor/*.py \
+               $(1)/usr/lib/python$(PYTHON3_VERSION)/site-packages/LibAppArmor
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)-libapparmor/usr/lib/python$(PYTHON3_VERSION)/site-packages/LibAppArmor/*.so \
+               $(1)/usr/lib/python$(PYTHON3_VERSION)/site-packages/LibAppArmor/
+       $(LN) -s _LibAppArmor.cpython-$(PYTHON3_VERSION_MAJOR)$(PYTHON3_VERSION_MINOR).so \
+               $(1)/usr/lib/python$(PYTHON3_VERSION)/site-packages/LibAppArmor/_LibAppArmor.so
+endef
+
+define Package/apparmor-utils/install
+       $(INSTALL_DIR) $(1)/etc/apparmor $(1)/usr/sbin $(1)/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)-utils/sbin/apparmor_parser $(1)/sbin/
+       $(INSTALL_CONF) $(PKG_INSTALL_DIR)-utils/etc/apparmor/*.conf $(1)/etc/apparmor/
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-utils/etc/apparmor/severity.db $(1)/etc/apparmor/
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)-utils/sbin/apparmor_parser $(1)/sbin/
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)-utils/usr/bin/{aa-exec,aa-easyprof,aa-enabled,aa-features-abi} $(1)/usr/sbin/
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)-utils/usr/sbin/{aa-audit,aa-autodep,aa-cleanprof,aa-complain,aa-decode,aa-disable,aa-enforce,aa-genprof,aa-logprof,aa-mergeprof,aa-notify,aa-remove-unknown,aa-status,aa-unconfined} $(1)/usr/sbin/
+       $(LN) aa-status $(1)/usr/sbin/apparmor_status
+       $(INSTALL_DIR) $(1)/usr/share/apparmor/easyprof/templates $(1)/usr/share/apparmor/easyprof/policygroups
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-utils/usr/share/apparmor/easyprof/templates/* $(1)/usr/share/apparmor/easyprof/templates/
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-utils/usr/share/apparmor/easyprof/policygroups/* $(1)/usr/share/apparmor/easyprof/policygroups/
+       $(INSTALL_DIR) $(1)/usr/lib/python3.9/site-packages $(1)/usr/lib/python3.9/site-packages/apparmor $(1)/usr/lib/python3.9/site-packages/apparmor/rule
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-utils/usr/lib/python3.9/site-packages/*.egg-info \
+               $(1)/usr/lib/python3.9/site-packages/
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-utils/usr/lib/python3.9/site-packages/apparmor/*.py \
+               $(1)/usr/lib/python3.9/site-packages/apparmor/
+       $(INSTALL_DATA) $(PKG_INSTALL_DIR)-utils/usr/lib/python3.9/site-packages/apparmor/rule/*.py \
+               $(1)/usr/lib/python3.9/site-packages/apparmor/rule
+       $(INSTALL_DIR) $(1)/etc/init.d $(1)/lib/functions
+       $(INSTALL_BIN) ./files/apparmor.sh $(1)/lib/functions/
+       $(INSTALL_BIN) ./files/apparmor.init $(1)/etc/init.d/apparmor
+endef
+
+define Package/apparmor-profiles/install
+       $(INSTALL_DIR) $(1)/etc/apparmor.d $(1)/usr/share/apparmor/extra-profiles
+       $(CP) -aR $(PKG_INSTALL_DIR)-profiles/etc/apparmor.d/** $(1)/etc/apparmor.d/
+       $(INSTALL_CONF) $(PKG_INSTALL_DIR)-profiles/usr/share/apparmor/extra-profiles/** $(1)/usr/share/apparmor/extra-profiles/
+endef
+
+$(eval $(call BuildPackage,libapparmor))
+$(eval $(call BuildPackage,python3-apparmor))
+$(eval $(call BuildPackage,apparmor-utils))
+$(eval $(call BuildPackage,apparmor-profiles))
diff --git a/utils/apparmor/files/apparmor.init b/utils/apparmor/files/apparmor.init
new file mode 100755 (executable)
index 0000000..576df39
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh /etc/rc.common
+
+START=75
+USE_PROCD=1
+
+. /lib/functions/apparmor.sh
+
+restart() {
+       apparmor_restart
+}
+
+start_service() {
+       apparmor_start
+}
+
+stop_service() {
+       apparmor_stop
+}
+
+reload_service() {
+       apparmor_reload
+}
diff --git a/utils/apparmor/files/apparmor.sh b/utils/apparmor/files/apparmor.sh
new file mode 100755 (executable)
index 0000000..5e9edb9
--- /dev/null
@@ -0,0 +1,380 @@
+#!/bin/sh
+
+log_write() {
+       local facility=kern.$1
+       logger -t AppArmor -p $facility "$2"
+}
+
+AA_STATUS=/usr/sbin/aa-status
+SECURITYFS=/sys/kernel/security
+SFS_MOUNTPOINT="${SECURITYFS}/apparmor"
+PARSER=/sbin/apparmor_parser
+PARSER_OPTS=
+ADDITIONAL_PROFILE_DIR=
+
+[ -d /etc/apparmor.d ] && PROFILE_DIRS=/etc/apparmor.d ||
+       log_write warning "Unable to find profiles: /etc/apparmor.d"
+
+[ -n "$ADDITIONAL_PROFILE_DIR" ] && [ -d "$ADDITIONAL_PROFILE_DIR" ] &&
+       PROFILE_DIRS="$PROFILE_DIRS $ADDITIONAL_PROFILE_DIR"
+
+dir_is_empty() {
+       [ "$(du -s $1 | cut -f 1)" -eq 0 ] && return 0 || return 1
+}
+
+profiles_loaded_count() {
+
+       [ -f ${SFS_MOUNTPOINT}/profiles ] &&
+               return $(cat "${SFS_MOUNTPOINT}/profiles" | wc -l) || return 0
+}
+
+is_profiles_loaded() {
+
+       [ -f ${SFS_MOUNTPOINT}/profiles ] && {
+               rc=$(cat "${SFS_MOUNTPOINT}/profiles" | wc -l)
+               [ "$rc" -ne 0 ] && return 0 || return 1
+       }
+       return 1
+}
+
+is_container_with_internal_policy() {
+
+       local ns_stacked_path="${SFS_MOUNTPOINT}/.ns_stacked"
+       local ns_name_path="${SFS_MOUNTPOINT}/.ns_name"
+       local ns_stacked
+       local ns_name
+
+       if ! [ -f "$ns_stacked_path" ] || ! [ -f "$ns_name_path" ]; then
+               return 1
+       fi
+
+       read -r ns_stacked < "$ns_stacked_path"
+       if [ "$ns_stacked" != "yes" ]; then
+               return 1
+       fi
+
+       # LXD and LXC set up AppArmor namespaces starting with "lxd-" and
+       # "lxc-", respectively. Return non-zero for all other namespace
+       # identifiers.
+
+       read -r ns_name < "$ns_name_path"
+       if [ "${ns_name#lxd-*}" = "$ns_name" ] && \
+          [ "${ns_name#lxc-*}" = "$ns_name" ]; then
+               return 1
+       fi
+
+       return 0
+}
+
+skip_profile() {
+
+       local profile="$1"
+
+       if [ "${profile%.rpmnew}"   != "$profile" ] || \
+          [ "${profile%.rpmsave}"  != "$profile" ] || \
+          [ "${profile%.orig}"     != "$profile" ] || \
+          [ "${profile%.rej}"      != "$profile" ] || \
+          [ "${profile%\~}"        != "$profile" ] ; then
+               return 1
+       fi
+
+       # Silently ignore the dpkg, pacman, ipk and xbps files
+
+       if [ "${profile%.dpkg-new}"     != "$profile" ] || \
+          [ "${profile%.dpkg-old}"     != "$profile" ] || \
+          [ "${profile%.dpkg-dist}"    != "$profile" ] || \
+          [ "${profile%.dpkg-bak}"     != "$profile" ] || \
+          [ "${profile%.dpkg-remove}"  != "$profile" ] || \
+          [ "${profile%.ipk}"          != "$profile" ] || \
+          [ "${profile%.ipk-new}"      != "$profile" ] || \
+          [ "${profile%.ipk-old}"      != "$profile" ] || \
+          [ "${profile%.ipk-dist}"     != "$profile" ] || \
+          [ "${profile%.ipk-bak}"      != "$profile" ] || \
+          [ "${profile%.ipk-remove}"   != "$profile" ] || \
+          [ "${profile%.pacsave}"      != "$profile" ] || \
+          [ "${profile%.pacnew}"       != "$profile" ] ; then
+               return 2
+       fi
+
+       $(echo "$profile" | grep -E -q '^.+\.new-[0-9\.]+_[0-9]+$'); [ "$?" -eq 0 ] && return 2
+
+       return 0
+}
+
+__parse_profiles_dir() {
+
+       local parser_cmd="$1"
+       local profile_dir="$2"
+       local status=0
+
+       [ -x "$PARSER" ] || {
+               log_write err "Unable to execute AppArmor parser"
+               return 1
+       }
+
+       [ -d "$profile_dir" ] || {
+               log_write warning "AppArmor profiles not found: $profile_dir"
+               return 1
+       }
+
+       dir_is_empty "$profile_dir"; [ "$?" -eq 0 ] && {
+               log_write err "No profiles found in $profile_dir"
+               return 1
+       }
+
+       local nprocs=$(cat /proc/cpuinfo |grep "processor\t:"|wc -l)
+       local rc=0
+       local xargs_args=""
+       [ "$nprocs" -ge 2 ] && xargs_args="--max-procs=$nprocs"
+
+       "$PARSER" $PARSER_OPTS "$parser_cmd" -- "$profile_dir" || {
+
+               for profile in "$profile_dir"/*; do
+                       skip_profile "$profile"
+                       skip=$?
+                       [ "$skip" -ne 0 ] && {
+                               [ "$skip" -ne 2 ] && log_write info "Skipped loading profile $profile"
+                               continue
+                       }
+                       [ -f "$profile" ] || continue
+                       echo "$profile"
+               done | \
+
+               # Use xargs to parallelize calls to the parser over all CPUs
+
+               /usr/libexec/xargs-findutils -n1 -d"\n" $xargs_args \
+                       "$PARSER" $PARSER_OPTS "$parser_cmd" --
+
+               [ "$?" -ne 0 ] && {
+                       rc=1
+                       log_write err "At least one profile failed to load"
+               }
+       }
+
+       return $rc
+}
+
+parse_profiles() {
+
+       case "$1" in
+               load)
+                       PARSER_CMD="--add"
+                       PARSER_MSG="Loading profiles"
+                       ;;
+               reload)
+                       PARSER_CMD="--replace"
+                       PARSER_MSG="Reloading profiles"
+                       ;;
+               *)
+                       log_write err "Unknown parameter $1"
+                       log_write info "parse_profiles parameter must be either 'load' or 'reload'"
+                       return 1
+                       ;;
+       esac
+
+       log_write info "$PARSER_MSG"
+
+       [ -w "$SFS_MOUNTPOINT/.load" ] || {
+               log_write err "${SFS_MOUNTPOINT}/.load not writable"
+               return 1
+       }
+
+       [ -f "$PARSER" ] || {
+               log_write err "AppArmor parser not found"
+               return 1
+       }
+
+       # run parser on all profiles
+       local rc=0
+       for profile_dir in $PROFILE_DIRS; do
+               __parse_profiles_dir "$PARSER_CMD" "$profile_dir" || rc=$?
+       done
+
+       return $rc
+}
+
+is_apparmor_loaded() {
+
+       is_securityfs_mounted; [ "$?" -eq 0 ] || {
+               mount_securityfs
+       }
+
+       [ -f "${SFS_MOUNTPOINT}/profiles" ] && return 0
+       [ -d /sys/module/apparmor ] && return 0 || return 1
+}
+
+is_securityfs_mounted() {
+
+       [ -d "$SECURITYFS" ] && {
+               grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
+               return $?
+       }
+       return 1
+}
+
+mount_securityfs() {
+
+       local rc=0
+
+       grep -q securityfs /proc/filesystems; [ "$?" -eq 0 ] && {
+               mount -t securityfs securityfs "$SECURITYFS"
+               rc=$?
+               [ "$rc" -eq 0 ] && log_write info "Mounting securityfs" ||
+                       log_write err "Failed to mount securityfs"
+       }
+       return $rc
+}
+
+apparmor_start() {
+
+       local announced=0
+       is_securityfs_mounted; [ "$?" -ne 0 ] && {
+               log_write info "Starting AppArmor"
+               announced=1
+               mount_securityfs; [ "$?" -eq 0 ] || return $?
+       }
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || {
+               [ "$announced" -eq 0 ] && log_write info "Starting AppArmor"
+               announced=1
+               log_write err "AppArmor kernel support is not present"
+               return 1
+       }
+
+       [ -d /var/lib/apparmor ] || mkdir -p /var/lib/apparmor > /dev/null
+
+       is_profiles_loaded; [ "$?" -eq 0 ] || {
+               [ "$announced" -eq 0 ] && log_write info "Starting AppArmor"
+               announced=1
+               parse_profiles load
+               return $?
+       } || {
+               parse_profiles reload
+               return $?
+       }
+}
+
+remove_profiles() {
+
+       log_write info "Unloading profiles"
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || {
+               log_write err "AppArmor kernel support is not present"
+               return 1
+       }
+
+       [ -w "$SFS_MOUNTPOINT/.remove" ] || {
+               log_write err "${SFS_MOUNTPOINT}/.remove not writable"
+               return 1
+       }
+
+       [ -x "$PARSER" ] || {
+               log_write err "Unable to execute AppArmor parser"
+               return 1
+       }
+
+       local rc=0
+
+       sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
+       LC_COLLATE=C sort | grep -v // | {
+               while read -r profile ; do
+                       printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"
+                       result=$?
+                       [ "$result" -eq 0 ] || rc=$result
+               done
+       }
+       return $rc
+}
+
+apparmor_stop() {
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || return 1
+       is_profiles_loaded; [ "$?" -eq 0 ] && {
+               log_write info "Stopping AppArmor"
+               remove_profiles
+               return $?
+       } || return 0
+}
+
+apparmor_restart() {
+
+       is_profiles_loaded; [ "$?" -eq 0 ] || {
+               apparmor_start
+               return $?
+       }
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || {
+               apparmor_start
+               return $?
+       }
+
+       log_write info "Restarting AppArmor"
+       parse_profiles reload
+       return $?
+}
+
+apparmor_reload() {
+
+       is_profiles_loaded; [ "$?" -eq 0 ] || {
+               apparmor_start
+               return $?
+       }
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || {
+               apparmor_start
+               return $?
+       }
+
+       log_write info "Reloading AppArmor"
+       parse_profiles reload
+       return $?
+}
+
+apparmor_list_profiles() {
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || {
+               echo "AppArmor kernel support is not present"
+               return 1
+       }
+
+        [ -x "$PARSER" ] || {
+                echo "Unable to execute AppArmor parser"
+               return 1
+       }
+
+       # run parser on all profiles
+       for profile_dir in $PROFILE_DIRS; do
+               [ -d "$profile_dir" ] || {
+                       echo "AppArmor profiles not found: $profile_dir"
+                       continue
+               }
+
+               for profile in "$profile_dir"/*; do
+                       if skip_profile "$profile" && [ -f "$profile" ] ; then
+                               LIST_ADD=$("$PARSER" -N "$profile" )
+                               [ "$?" -eq 0 ] && echo "$LIST_ADD"
+                       fi
+               done
+       done
+       return 0
+}
+
+
+apparmor_status() {
+
+       is_apparmor_loaded; [ "$?" -eq 0 ] || {
+               echo "AppArmor kernel support is not present"
+               return 1
+       }
+
+       [ -x "$AA_STATUS" ] && {
+               "$AA_STATUS" --verbose
+               return $?
+       }
+
+       echo "AppArmor is enabled."
+       echo "Install apparmor-utils to receive more detailed status"
+       echo "information or examine $SFS_MOUNTPOINT directly."
+
+       return 0
+}
diff --git a/utils/apparmor/patches/010-autoconf-libapparmor.patch b/utils/apparmor/patches/010-autoconf-libapparmor.patch
new file mode 100644 (file)
index 0000000..bcb47d0
--- /dev/null
@@ -0,0 +1,10 @@
+--- /dev/null
++++ b/libraries/libapparmor/Makefile
+@@ -0,0 +1,7 @@
++package=libapparmor
++
++configure:
++      $(STAGING_DIR_HOST)/bin/aclocal
++      $(STAGING_DIR_HOST)/bin/autoconf --force
++      $(STAGING_DIR_HOST)/bin/libtoolize --automake -c --force
++      $(STAGING_DIR_HOST)/bin/automake -ac
diff --git a/utils/apparmor/patches/020-fix-ss-path.patch b/utils/apparmor/patches/020-fix-ss-path.patch
new file mode 100644 (file)
index 0000000..11e53b0
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/utils/aa-unconfined
++++ b/utils/aa-unconfined
+@@ -118,7 +118,7 @@ def read_proc_current(filename):
+ pids = set()
+ if paranoid:
+     pids = get_all_pids()
+-elif args.with_ss or (not args.with_netstat and (os.path.exists('/bin/ss') or os.path.exists('/usr/bin/ss'))):
++elif args.with_ss or (not args.with_netstat and (os.path.exists('/usr/sbin/ss') or os.path.exists('/bin/ss') or os.path.exists('/usr/bin/ss') or os.path.exists('/sbin/ss'))):
+     pids = get_pids_ss()
+ else:
+     pids = get_pids_netstat()
diff --git a/utils/apparmor/patches/030-remove-pynotify2-dep.patch b/utils/apparmor/patches/030-remove-pynotify2-dep.patch
new file mode 100644 (file)
index 0000000..7de4ddc
--- /dev/null
@@ -0,0 +1,416 @@
+--- a/utils/aa-notify
++++ b/utils/aa-notify
+@@ -13,17 +13,6 @@
+ #
+ # ----------------------------------------------------------------------
+ #
+-# /etc/apparmor/notify.conf:
+-# # set to 'yes' to enable AppArmor DENIED notifications
+-# show_notifications="yes"
+-#
+-# # only people in use_group can run this script
+-# use_group="admin"
+-#
+-# $HOME/.apparmor/notify.conf can have:
+-# # set to 'yes' to enable AppArmor DENIED notifications
+-# show_notifications="yes"
+-#
+ # In a typical desktop environment one would run as a service the
+ # command:
+ #   /usr/bin/aa-notify -p -w 10
+@@ -35,7 +24,6 @@ import re
+ import sys
+ import time
+ import struct
+-import notify2
+ import psutil
+ import pwd
+ import grp
+@@ -60,56 +48,9 @@ def get_user_login():
+             username = os.getlogin()
+     return username
+-
+-def get_last_login_timestamp(username):
+-    '''Directly read wtmp and get last login for user as epoch timestamp'''
+-    timestamp = 0
+-    filename = '/var/log/wtmp'
+-    last_login = 0
+-
+-    debug_logger.debug('Username: {}'.format(username))
+-
+-    with open(filename, "rb") as wtmp_file:
+-        offset = 0
+-        wtmp_filesize = os.path.getsize(filename)
+-        debug_logger.debug('WTMP filesize: {}'.format(wtmp_filesize))
+-        while offset < wtmp_filesize:
+-            wtmp_file.seek(offset)
+-            offset += 384  # Increment for next entry
+-
+-            type = struct.unpack("<L", wtmp_file.read(4))[0]
+-            debug_logger.debug('WTMP entry type: {}'.format(type))
+-
+-            # Only parse USER lines
+-            if type == 7:
+-                # Read each item and move pointer forward
+-                pid = struct.unpack("<L", wtmp_file.read(4))[0]
+-                line = wtmp_file.read(32).decode("utf-8", "replace").split('\0', 1)[0]
+-                id = wtmp_file.read(4).decode("utf-8", "replace").split('\0', 1)[0]
+-                user = wtmp_file.read(32).decode("utf-8", "replace").split('\0', 1)[0]
+-                host = wtmp_file.read(256).decode("utf-8", "replace").split('\0', 1)[0]
+-                term = struct.unpack("<H", wtmp_file.read(2))[0]
+-                exit = struct.unpack("<H", wtmp_file.read(2))[0]
+-                session = struct.unpack("<L", wtmp_file.read(4))[0]
+-                timestamp = struct.unpack("<L", wtmp_file.read(4))[0]
+-                usec = struct.unpack("<L", wtmp_file.read(4))[0]
+-                entry = (pid, line, id, user, host, term, exit, session, timestamp, usec)
+-                debug_logger.debug('WTMP entry: {}'.format(entry))
+-
+-                # Store login timestamp for requested user
+-                if user == username:
+-                    last_login = timestamp
+-
+-    # When loop is done, last value should be the latest login timestamp
+-    return last_login
+-
+-
+ def format_event(event, logsource):
+     output = []
+-    if 'message_body' in config['']:
+-        output += [config['']['message_body']]
+-
+     if event.profile:
+         output += ['Profile: {}'.format(event.profile)]
+     if event.operation:
+@@ -126,7 +67,6 @@ def format_event(event, logsource):
+     return "\n".join(output)
+-
+ def notify_about_new_entries(logfile, wait=0):
+     # Kill other instances of aa-notify if already running
+     for process in psutil.process_iter():
+@@ -154,7 +94,6 @@ def notify_about_new_entries(logfile, wa
+         # print("parent: %d, child: %d\n" % pids)
+         os._exit(0)  # Exit child without calling exit handlers etc
+-
+ def show_entries_since_epoch(logfile, epoch_since):
+     count = 0
+     for event in get_apparmor_events(logfile, epoch_since):
+@@ -172,26 +111,7 @@ def show_entries_since_epoch(logfile, ep
+     )
+     if args.verbose:
+-        if 'message_footer' in config['']:
+-            print(config['']['message_footer'])
+-        else:
+-            print(_('For more information, please see: {}').format(debug_docs_url))
+-
+-
+-def show_entries_since_last_login(logfile, username=get_user_login()):
+-    # If running as sudo, use username of sudo user instead of root
+-    if 'SUDO_USER' in os.environ.keys():
+-        username = os.environ['SUDO_USER']
+-
+-    if args.verbose:
+-        print(_('Showing entries since {} logged in').format(username))
+-        print() # Newline
+-    epoch_since = get_last_login_timestamp(username)
+-    if epoch_since == 0:
+-        print(_('ERROR: Could not find last login'), file=sys.stderr)
+-        sys.exit(1)
+-    show_entries_since_epoch(logfile, epoch_since)
+-
++        print(_('For more information, please see: {}').format(debug_docs_url))
+ def show_entries_since_days(logfile, since_days):
+     day_in_seconds = 60*60*24
+@@ -199,7 +119,6 @@ def show_entries_since_days(logfile, sin
+     epoch_since = epoch_now - day_in_seconds * since_days
+     show_entries_since_epoch(logfile, epoch_since)
+-
+ def follow_apparmor_events(logfile, wait=0):
+     '''Follow AppArmor events and yield relevant entries until process stops'''
+@@ -247,7 +166,6 @@ def follow_apparmor_events(logfile, wait
+             time.sleep(1)
+-
+ def reopen_logfile_if_needed(logfile, logdata, log_inode, log_size):
+     retry = True
+@@ -279,7 +197,6 @@ def reopen_logfile_if_needed(logfile, lo
+     return (logdata, log_inode, log_size)
+-
+ def get_apparmor_events(logfile, since=0):
+     '''Read audit events from log source and yield all relevant events'''
+@@ -293,7 +210,6 @@ def get_apparmor_events(logfile, since=0
+     except PermissionError:
+         sys.exit(_("ERROR: Cannot read {}. Please check permissions.".format(logfile)))
+-
+ def parse_logdata(logsource):
+     '''Traverse any iterable log source and extract relevant AppArmor events'''
+@@ -327,53 +243,6 @@ def parse_logdata(logsource):
+             if event.operation[0:8] != 'profile_':
+                 yield event
+-
+-def drop_privileges():
+-    '''If running as root, drop privileges to USER if known, or fall-back to nobody_user/group'''
+-
+-    if os.geteuid() == 0:
+-
+-        if 'SUDO_USER' in os.environ.keys():
+-            next_username = os.environ['SUDO_USER']
+-            next_uid = os.environ['SUDO_UID']
+-            next_gid = os.environ['SUDO_GID']
+-        else:
+-            nobody_user_info = pwd.getpwnam(nobody_user)
+-            next_username = nobody_user_info[0]
+-            next_uid = nobody_user_info[2]
+-            next_gid = nobody_user_info[3]
+-
+-        debug_logger.debug('Dropping to user "{}" privileges'.format(next_username))
+-
+-        # @TODO?
+-        # Remove group privileges, including potential 'adm' group that might
+-        # have had log read access but also other accesses.
+-        # os.setgroups([])
+-
+-        # Try setting the new uid/gid
+-        # Set gid first, otherwise the latter step would fail on missing permissions
+-        os.setegid(int(next_gid))
+-        os.seteuid(int(next_uid))
+-
+-def raise_privileges():
+-    '''If was running as user with saved user ID 0, raise back to root privileges'''
+-
+-    if os.geteuid() != 0 and original_effective_user == 0:
+-
+-        debug_logger.debug('Rasing privileges from UID {} back to UID 0 (root)'.format(os.geteuid()))
+-
+-        # os.setgid(int(next_gid))
+-        os.seteuid(original_effective_user)
+-
+-def read_notify_conf(path, shell_config):
+-    try:
+-        shell_config.CONF_DIR = path
+-        conf_dict = shell_config.read_config('notify.conf')
+-        debug_logger.debug('Found configuration file in {}/notify.conf'.format(shell_config.CONF_DIR))
+-        return conf_dict
+-    except FileNotFoundError:
+-        return {}
+-
+ def main():
+     '''
+     Main function of aa-notify that parses command line
+@@ -381,10 +250,9 @@ def main():
+     '''
+     global _, debug_logger, config, args
+-    global debug_docs_url, nobody_user, original_effective_user, timeformat
++    global debug_docs_url, original_effective_user, timeformat
+     debug_docs_url = "https://wiki.ubuntu.com/DebuggingApparmor"
+-    nobody_user = "nobody"
+     timeformat = "%c"  # Automatically using locale format
+     original_effective_user = os.geteuid()
+@@ -403,180 +271,37 @@ def main():
+     debug_logger.debug("Starting aa-notify")
+     parser = argparse.ArgumentParser(description=_('Display AppArmor notifications or messages for DENIED entries.'))
+-    parser.add_argument('-p', '--poll', action='store_true', help=_('poll AppArmor logs and display notifications'))
+-    parser.add_argument('--display', type=str, help=_('set the DISPLAY environment variable (might be needed if sudo resets $DISPLAY)'))
+-    parser.add_argument('-f', '--file', type=str, help=_('search FILE for AppArmor messages'))
+-    parser.add_argument('-l', '--since-last', action='store_true', help=_('display stats since last login'))
+-    parser.add_argument('-s', '--since-days', type=int, metavar=('NUM'), help=_('show stats for last NUM days (can be used alone or with -p)'))
+-    parser.add_argument('-v', '--verbose', action='store_true', help=_('show messages with stats'))
+-    parser.add_argument('-u', '--user', type=str, help=_('user to drop privileges to when not using sudo'))
+-    parser.add_argument('-w', '--wait', type=int, metavar=('NUM'), help=_('wait NUM seconds before displaying notifications (with -p)'))
+-    parser.add_argument('--debug', action='store_true', help=_('debug mode'))
+-    parser.add_argument('--configdir', type=str, help=argparse.SUPPRESS)
++    parser.add_argument('-f', '--file', type=str, help=_('Logfile to parse for AppArmor messages'))
++    parser.add_argument('-s', '--since-days', type=int, metavar=('NUM'), help=_('Show stats for last NUM days'))
++    parser.add_argument('-v', '--verbose', action='store_true', help=_('Show messages with stats'))
++    parser.add_argument('--debug', action='store_true', help=_('Debug mode'))
+     # If a TTY then assume running in test mode and fix output width
+     if not sys.stdout.isatty():
+         parser.formatter_class = lambda prog: argparse.HelpFormatter(prog, width=80)
+     args = parser.parse_args()
++    args.user = 'root'
+     # Debug mode can be invoked directly with --debug or env LOGPROF_DEBUG=3
+     if args.debug:
+         debug_logger.activateStderr()
+         debug_logger.debug('Logging level: {}'.format(debug_logger.debug_level))
+         debug_logger.debug('Running as uid: {0[0]}, euid: {0[1]}, suid: {0[2]}'.format(os.getresuid()))
+-        if args.poll:
+-            debug_logger.debug('Running with --debug and --poll. Will exit in 100s')
+-    # Sanity checks
+-    user_ids = os.getresuid()
+-    groups_ids = os.getresgid()
+-    if user_ids[1] != user_ids[2]:
+-        sys.exit("ERROR: Cannot be started with suid set!")
+-    if groups_ids[1] != groups_ids[2]:
+-        sys.exit("ERROR: Cannot be started with sgid set!")
+-    # Define global variables that will be populated by init_aa()
+-    # conf = None
+     logfile = None
+-    if args.configdir:  # prefer --configdir if given
+-        confdir = args.configdir
+-    else:  # fallback to env variable (or None if not set)
+-        confdir = os.getenv('__AA_CONFDIR')
+-
+-    aa.init_aa(confdir=confdir)
+-
+     # Initialize aa.logfile
+-    aa.set_logfile(args.file)
+-
+-    # Load global config reader
+-    shell_config = aaconfig.Config('shell')
+-
+-    # Load system's notify.conf
+-    # By default aa.CONFDIR is /etc/apparmor on most production systems
+-    system_config = read_notify_conf(aa.CONFDIR, shell_config)
+-    # Set default is no system notify.conf was found
+-    if not system_config:
+-        system_config = {'': {'show_notifications': 'yes'}}
+-
+-    # Load user's notify.conf
+-    if os.path.isfile(os.environ['HOME'] + '/.apparmor/notify.conf'):
+-        # Use legacy path if the conf file is there
+-        user_config = read_notify_conf(os.environ['HOME'] + '/.apparmor', shell_config)
+-    elif 'XDG_CONFIG_HOME' in os.environ and os.path.isfile(os.environ['XDG_CONFIG_HOME'] + '/apparmor/notify.conf'):
+-        # Use XDG_CONFIG_HOME if it is defined
+-        user_config = read_notify_conf(os.environ['XDG_CONFIG_HOME'] + '/apparmor', shell_config)
+-    else:
+-        # Fallback to the default value of XDG_CONFIG_HOME
+-        user_config = read_notify_conf(os.environ['HOME'] + '/.config/apparmor', shell_config)
+-
+-    # Merge the two config dicts in an accurate and idiomatic way (requires Python 3.5)
+-    config = {**system_config, **user_config}
+-
+-    """
+-    Possible configuration options:
+-    - show_notifications
+-    - message_body
+-    - message_footer
+-    - use_group
+-    """
+-
+-    # # Config checks
+-
+-    # Warn about unknown keys in the config
+-    allowed_config_keys = [
+-        'use_group',
+-        'show_notifications',
+-        'message_body',
+-        'message_footer'
+-    ]
+-    found_config_keys = config[''].keys()
+-    unknown_keys = [item for item in found_config_keys if item not in allowed_config_keys]
+-    for item in unknown_keys:
+-        print(_('Warning! Configuration item "{}" is unknown!').format(item))
+-
+-    # Warn if use_group is defined and current group does not match defined
+-    if 'use_group' in config['']:
+-        user = pwd.getpwuid(os.geteuid())[0]
+-        user_groups = [g.gr_name for g in grp.getgrall() if user in g.gr_mem]
+-        gid = pwd.getpwnam(user).pw_gid
+-        user_groups.append(grp.getgrgid(gid).gr_name)
+-
+-        if config['']['use_group'] not in user_groups:
+-            print(
+-                _('ERROR! User {user} not member of {group} group!').format(
+-                    user=user,
+-                    group=config['']['use_group']
+-                ),
+-                file=sys.stderr
+-            )
+-            sys.exit(1)
+-            # @TODO: Extend UI lib to have warning and error functions that
+-            # can be used in an uniform way with both text and JSON output.
+-
+     if args.file:
+         logfile = args.file
+-    elif os.path.isfile('/var/run/auditd.pid') and os.path.isfile('/var/log/audit/audit.log'):
+-        # If auditd is running, look at /var/log/audit/audit.log
+-        logfile = '/var/log/audit/audit.log'
+-    elif os.path.isfile('/var/log/kern.log'):
+-        # For aa-notify, the fallback is kern.log, not syslog from aa.logfile
+-        logfile = '/var/log/kern.log'
++        aa.set_logfile(args.file)
+     else:
+-        # If all above failed, use aa cfg
+-        logfile = aa.logfile
++        logfile = '/var/log/audit/audit.log'
++        aa.set_logfile('/var/log/audit/audit.log')
+     if args.verbose:
+         print(_('Using log file'), logfile)
+-    if args.display:
+-        os.environ['DISPLAY'] = args.display
+-
+-    if args.poll:
+-        # Exit immediately if show_notifications is no or any of the options below
+-        if config['']['show_notifications'] in [False, 'no', 'false', '0']:
+-            print(_('Showing notifications forbidden in notify.conf, aborting..'))
+-            sys.exit(0)
+-
+-        # Don't allow usage of aa-notify by root, must be some user. Desktop
+-        # logins as root are not recommended and certainly not a use case for
+-        # aa-notify notifications.
+-        if not args.user and os.getuid() == 0 and 'SUDO_USER' not in os.environ.keys():
+-            sys.exit("ERROR: Cannot be started a real root user. Use --user to define what user to use.")
+-
+-        # At this point this script needs to be able to read 'logfile' but once
+-        # the for loop starts, privileges can be dropped since the file descriptor
+-        # has been opened and access granted. Further reads of the file will not
+-        # trigger any new permission checks.
+-        # @TODO Plan to catch PermissionError here or..?
+-        for message in notify_about_new_entries(logfile, args.wait):
+-
+-            # Notifications should not be run as root, since root probably is
+-            # the wrong desktop user and not the one getting the notifications.
+-            drop_privileges()
+-
+-            # sudo does not preserve DBUS address, so we need to guess it based on UID
+-            if 'DBUS_SESSION_BUS_ADDRESS' not in os.environ:
+-                os.environ['DBUS_SESSION_BUS_ADDRESS'] = 'unix:path=/run/user/{}/bus'.format(os.geteuid())
+-
+-            # Before use, notify2 must be initialized and the DBUS channel
+-            # should be opened using the non-root user. This this step needs to
+-            # be executed after the drop_privileges().
+-            notify2.init('AppArmor')
+-
+-            n = notify2.Notification(
+-                _('AppArmor notification'),
+-                message,
+-                'gtk-dialog-warning'
+-            )
+-            n.show()
+-
+-            # When notification is sent, raise privileged back to root if the
+-            # original effective user id was zero (to be able to read AppArmor logs)
+-            raise_privileges()
+-
+-    elif args.since_last:
+-        show_entries_since_last_login(logfile)
+     elif args.since_days:
+         show_entries_since_days(logfile, args.since_days)
+     else:
diff --git a/utils/apparmor/patches/040-remove-bash-dep.patch b/utils/apparmor/patches/040-remove-bash-dep.patch
new file mode 100644 (file)
index 0000000..98ef107
--- /dev/null
@@ -0,0 +1,134 @@
+--- a/utils/aa-decode
++++ b/utils/aa-decode
+@@ -1,4 +1,4 @@
+-#!/bin/bash
++#!/bin/sh
+ #
+ #    Copyright (C) 2009-2010, 2012 Canonical Ltd.
+ #    Copyright (C) 2012 Christian Boltz
+@@ -16,8 +16,6 @@
+ #    along with this program; if not, contact Canonical, Ltd.
+ #
+-set -e
+-
+ help() {
+     cat <<EOM
+ USAGE: aa-decode [OPTIONS] <encoded string>
+@@ -36,13 +34,15 @@ $ cat /var/log/kern.log | aa-decode
+ EOM
+ }
+-decode() {
+-    if echo "$1" | egrep -q "^[0-9A-Fa-f]+$" ; then
+-      python3 -c "import binascii; print(bytes.decode(binascii.unhexlify('$1'), errors='strict'));"
+-    else
+-      echo ""
+-    fi
++match_re() {
++      local result=$(echo "$1" | grep -E "$2" )
++      [ -z "$result" ] && return 1 || return 0
++}
++
++decode() {
++      $(echo "$1" | egrep -q "^[0-9A-Fa-f]+$"); [ "$?" -eq 0 ] &&
++              python3 -c "import binascii; print(bytes.decode(binascii.unhexlify('$1'), errors='strict'));" || echo ""
+ }
+ if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
+@@ -51,47 +51,61 @@ if [ "$1" = "-h" ] || [ "$1" = "--help"
+ fi
+ # if have an argument, then use it, otherwise process stdin
+-if [ -n "$1" ]; then
+-    e="$1"
+-    if ! echo "$e" | egrep -q "^[0-9A-Fa-f]+$" ; then
+-        echo "String should only contain hex characters (0-9, a-f, A-F)"
+-        exit 1
+-    fi
+-
+-    d=`decode $e`
+-    if [ -z "$d" ]; then
+-        echo "Could not decode string"
+-        exit 1
+-    fi
++[ -n "$1" ] && {
+-    echo "Decoded: $d"
+-    exit 0
+-fi
++      e="$1"
+-# For now just look at 'name=...' and 'profile=...',
+-# so validate input against this and output based on it.
+-# TODO: better handle other cases too
+-while read line ; do
++      $(echo "$e" | egrep -q "^[0-9A-Fa-f]+$"); [ "$?" -ne 0 ] && {
++              echo "String should only contain hex characters (0-9, a-f, A-F)"
++              exit 1
++      }
+-    # check if line contains encoded name= or profile=
+-    if [[ "$line" =~ \ (name|profile|proctitle)=[0-9a-fA-F] ]]; then
++      d=$(decode $e)
+-        # cut the encoded filename/profile name out of the line and decode it
+-        ne=`echo "$line" | sed 's/.* name=\([^ ]*\).*$/\\1/g'`
+-        nd="$(decode ${ne/\'/\\\'})"
++      [ -z "$d" ] && {
++              echo "Could not decode string"
++              exit 1
++      }
+-        pe=`echo "$line" | sed 's/.* profile=\([^ ]*\).*$/\\1/g'`
+-        pd="$(decode ${pe/\'/\\\'})"
++      echo "Decoded: $d"
++      exit 0
++}
+-        pce=`echo "$line" | sed 's/.* proctitle=\([^ ]*\).*$/\\1/g'`
+-        pcd="$(decode ${pce/\'/\\\'})"
++[ -t 0 ] && {
++      help
++      exit
++}
++
++while read line ; do
+-        # replace encoded name and profile with its decoded counterparts (only if it was encoded)
+-        test -n "$nd" && line="${line/name=$ne/name=\"$nd\"}"
+-        test -n "$pd" && line="${line/profile=$pe/profile=\"$pd\"}"
+-        test -n "$pcd" && line="${line/proctitle=$pce/proctitle=\"$pcd\"}"
++      # check if line contains encoded name= or profile=
+-    fi
++      matches=0
++      match_re "$line" "^[[:blank:]](name|profile|proctitle)=[0-9a-fA-F]+"; [ "$?" -eq 0 ] && matches=1 || {
++               match_re "$line" "^(name|profile|proctitle)=[0-9a-fA-F]+"; [ "$?" -eq 0 ] && matches=1
++      }
++
++      [ "$matches" -eq 0 ] || {
++
++              # cut the encoded filename/profile name out of the line and decode it
++              ne=$(echo "$line" | sed 's/.* name=\([^ ]*\).*$/\\1/g')
++              [ "$line" = "$ne" ] && ne=$(echo "$line" | sed 's/.*name=\([^ ]*\).*$/\\1/g')
++              echo var: $ne
++              nd="$(decode ${ne/\'/\\\'})"
++
++              pe=$(echo "$line" | sed 's/.* profile=\([^ ]*\).*$/\\1/g')
++              [ "$line" = "$pe" ] && pe=$(echo "$line" | sed 's/.*profile=\([^ ]*\).*$/\\1/g')
++              pd="$(decode ${pe/\'/\\\'})"
++
++              pce=$(echo "$line" | sed 's/.* proctitle=\([^ ]*\).*$/\\1/g')
++              [ "$line" = "$pce" ] && pce=$(echo "$line" | sed 's/.*proctitle=\([^ ]*\).*$/\\1/g')
++              pcd="$(decode ${pce/\'/\\\'})"
++
++              # replace encoded name and profile with its decoded counterparts (only if it was encoded)
++              test -n "$nd" && line="${line/name=$ne/name=\"$nd\"}"
++              test -n "$pd" && line="${line/profile=$pe/profile=\"$pd\"}"
++              test -n "$pcd" && line="${line/proctitle=$pce/proctitle=\"$pcd\"}"
++      }
+     echo "$line"
diff --git a/utils/apparmor/patches/050-disable-man-pages.patch b/utils/apparmor/patches/050-disable-man-pages.patch
new file mode 100644 (file)
index 0000000..a0463f1
--- /dev/null
@@ -0,0 +1,69 @@
+--- a/binutils/Makefile
++++ b/binutils/Makefile
+@@ -107,7 +107,7 @@ docs:      manpages
+ indep: docs
+       $(Q)$(MAKE) -C po all
+-all:  arch indep
++all:  arch
+ .PHONY: coverage
+ coverage:
+@@ -147,7 +147,7 @@ tests: $(BINTOOLS) $(SBINTOOLS) $(TESTS)
+       echo "no tests atm"
+ .PHONY: install
+-install: install-indep install-arch
++install: install-arch
+ .PHONY: install-arch
+ install-arch: arch
+--- a/parser/Makefile
++++ b/parser/Makefile
+@@ -195,7 +195,7 @@ extra_docs: pdf
+ indep: docs
+       $(Q)$(MAKE) -C po all
+-all:  arch indep
++all:  arch
+ .PHONY: coverage
+ coverage:
+@@ -396,7 +396,6 @@ endif
+ .PHONY: install
+ install:
+-      $(MAKE) install-indep
+       $(MAKE) install-arch
+ .PHONY: install-arch
+--- a/utils/Makefile
++++ b/utils/Makefile
+@@ -31,9 +31,7 @@ MANPAGES = ${TOOLS:=.8} logprof.conf.5
+ PYFLAKES ?= pyflakes3
+-all: docs
+-      $(MAKE) -C po all
+-      $(MAKE) -C vim all
++all: clean
+ .PHONY: docs
+ docs: ${MANPAGES} ${HTMLMANPAGES}
+@@ -49,15 +47,12 @@ po/${NAME}.pot: ${TOOLS} ${PYMODULES}
+       $(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${TOOLS} ${PYMODULES}"
+ .PHONY: install
+-install: ${MANPAGES} ${HTMLMANPAGES}
++install:
+       install -d ${CONFDIR}
+       install -m 644 logprof.conf severity.db notify.conf ${CONFDIR}
+       install -d ${BINDIR}
+       # aa-easyprof is installed by python-tools-setup.py
+       install -m 755 $(filter-out aa-easyprof, ${TOOLS}) ${BINDIR}
+-      $(MAKE) -C po install DESTDIR=${DESTDIR} NAME=${NAME}
+-      $(MAKE) install_manpages DESTDIR=${DESTDIR}
+-      $(MAKE) -C vim install DESTDIR=${DESTDIR}
+       ${PYTHON} ${PYSETUP} install --prefix=${PYPREFIX} --root=${DESTDIR} --version=${VERSION}
+ .PHONY: clean
diff --git a/utils/apparmor/patches/060-openwrt-dnsmasq-profile.patch b/utils/apparmor/patches/060-openwrt-dnsmasq-profile.patch
new file mode 100644 (file)
index 0000000..cc957ec
--- /dev/null
@@ -0,0 +1,190 @@
+--- a/profiles/apparmor.d/usr.sbin.dnsmasq
++++ b/profiles/apparmor.d/usr.sbin.dnsmasq
+@@ -1,3 +1,10 @@
++# Last Modified: Thu Jun 10 01:23:44 2021
++abi <abi/3.0>,
++
++include <tunables/global>
++
++@{TFTP_DIR} = /srv/tftp /srv/tftpboot /var/tftp
++
+ # ------------------------------------------------------------------
+ #
+ #    Copyright (C) 2009 John Dong <jdong@ubuntu.com>
+@@ -9,126 +16,95 @@
+ #
+ # ------------------------------------------------------------------
+-abi <abi/3.0>,
+-
+-@{TFTP_DIR}=/var/tftp /srv/tftp /srv/tftpboot
+-include <tunables/global>
+ profile dnsmasq /usr/{bin,sbin}/dnsmasq flags=(attach_disconnected) {
+   include <abstractions/base>
+   include <abstractions/dbus>
+   include <abstractions/nameservice>
++  include <abstractions/user-tmp>
++  include if exists <local/usr.sbin.dnsmasq>
+   capability chown,
++  capability dac_override,
++  capability net_admin, # for DHCP server
+   capability net_bind_service,
++  capability net_raw, # for DHCP server ping checks
+   capability setgid,
+   capability setuid,
+-  capability dac_override,
+-  capability net_admin,         # for DHCP server
+-  capability net_raw,           # for DHCP server ping checks
++
+   network inet raw,
+   network inet6 raw,
+-  signal (receive) peer=/usr/{bin,sbin}/libvirtd,
+-  signal (receive) peer=libvirtd,
+-  ptrace (readby) peer=/usr/{bin,sbin}/libvirtd,
+-  ptrace (readby) peer=libvirtd,
++  signal receive peer=/usr/{bin,sbin}/libvirtd,
++  signal receive peer=libvirtd,
+-  owner /dev/tty rw,
++  ptrace readby peer=/usr/{bin,sbin}/libvirtd,
++  ptrace readby peer=libvirtd,
+-  @{PROC}/@{pid}/fd/ r,
+-
+-  /etc/dnsmasq.conf r,
+-  /etc/dnsmasq.d/ r,
+-  /etc/dnsmasq.d/* r,
+-  /etc/dnsmasq.d-available/ r,
+-  /etc/dnsmasq.d-available/* r,
+-  /etc/ethers r,
+-  /etc/NetworkManager/dnsmasq.d/ r,
+-  /etc/NetworkManager/dnsmasq.d/* r,
+   /etc/NetworkManager/dnsmasq-shared.d/ r,
+   /etc/NetworkManager/dnsmasq-shared.d/* r,
++  /etc/NetworkManager/dnsmasq.d/ r,
++  /etc/NetworkManager/dnsmasq.d/* r,
+   /etc/dnsmasq-conf.conf r,
+   /etc/dnsmasq-resolv.conf r,
+-
+-  /usr/{bin,sbin}/dnsmasq mr,
+-
+-  /var/log/dnsmasq*.log w,
+-
++  /etc/dnsmasq.conf r,
++  /etc/dnsmasq.d-available/ r,
++  /etc/dnsmasq.d-available/* r,
++  /etc/dnsmasq.d/ r,
++  /etc/dnsmasq.d/* r,
++  /etc/ethers r,
++  /tmp/** r,
++  /usr/libexec/libvirt_leaseshelper Cx -> libvirt_leaseshelper,
++  /usr/lib{,64}/libvirt/libvirt_leaseshelper Cx -> libvirt_leaseshelper,
+   /usr/share/dnsmasq{-base,}/ r,
+   /usr/share/dnsmasq{-base,}/* r,
+-
+-  @{run}/*dnsmasq*.pid w,
+-  @{run}/dnsmasq-forwarders.conf r,
+-  @{run}/dnsmasq/ r,
+-  @{run}/dnsmasq/* rw,
+-
++  /usr/{bin,sbin}/dnsmasq mr,
++  /var/lib/NetworkManager/dnsmasq-*.leases rw,
++  /var/lib/libvirt/dnsmasq/ r,
++  /var/lib/libvirt/dnsmasq/* r,
++  /var/lib/lxd-bridge/dnsmasq.*.leases rw,
++  /var/lib/lxd/networks/*/dnsmasq.* r,
++  /var/lib/lxd/networks/*/dnsmasq.leases rw,
++  /var/lib/lxd/networks/*/dnsmasq.pid rw,
++  /var/lib/misc/dnsmasq.*.leases rw,
+   /var/lib/misc/dnsmasq.leases rw, # Required only for DHCP server usage
+-
++  /var/log/dnsmasq*.log w,
+   /{,usr/}bin/{ba,da,}sh ix, # Required to execute --dhcp-script argument
+-
+-  # access to iface mtu needed for Router Advertisement messages in IPv6
+-  # Neighbor Discovery protocol (RFC 2461)
++  @{PROC}/@{pid}/fd/ r,
+   @{PROC}/sys/net/ipv6/conf/*/mtu r,
+-
+-  # for the read-only TFTP server
+   @{TFTP_DIR}/ r,
+   @{TFTP_DIR}/** r,
+-
+-  # libvirt config and hosts file for dnsmasq
+-  /var/lib/libvirt/dnsmasq/          r,
+-  /var/lib/libvirt/dnsmasq/*         r,
+-
+-  # libvirt pid files for dnsmasq
+-  @{run}/libvirt/network/      r,
++  @{run}/*dnsmasq*.pid w,
++  @{run}/NetworkManager/NetworkManager.pid w,
++  @{run}/NetworkManager/dnsmasq.conf r,
++  @{run}/NetworkManager/dnsmasq.pid w,
++  @{run}/dnsmasq-forwarders.conf r,
++  @{run}/dnsmasq/ r,
++  @{run}/dnsmasq/* rw,
++  @{run}/libvirt/network/ r,
+   @{run}/libvirt/network/*.pid rw,
+-
+-  # libvirt lease helper
+-  /usr/lib{,64}/libvirt/libvirt_leaseshelper Cx -> libvirt_leaseshelper,
+-  /usr/libexec/libvirt_leaseshelper Cx -> libvirt_leaseshelper,
+-
+-  # lxc-net pid and lease files
+-  @{run}/lxc/dnsmasq.pid    rw,
+-  /var/lib/misc/dnsmasq.*.leases rw,
+-
+-  # lxd-bridge pid and lease files
+-  @{run}/lxd-bridge/dnsmasq.pid   rw,
+-  /var/lib/lxd-bridge/dnsmasq.*.leases rw,
+-  /var/lib/lxd/networks/*/dnsmasq.* r,
+-  /var/lib/lxd/networks/*/dnsmasq.leases rw,
+-  /var/lib/lxd/networks/*/dnsmasq.pid rw,
+-
+-  # NetworkManager integration
+-  /var/lib/NetworkManager/dnsmasq-*.leases rw,
++  @{run}/lxc/dnsmasq.pid rw,
++  @{run}/lxd-bridge/dnsmasq.pid rw,
+   @{run}/nm-dns-dnsmasq.conf r,
+   @{run}/nm-dnsmasq-*.pid rw,
+   @{run}/sendsigs.omit.d/*dnsmasq.pid w,
+-  @{run}/NetworkManager/dnsmasq.conf r,
+-  @{run}/NetworkManager/dnsmasq.pid w,
+-  @{run}/NetworkManager/NetworkManager.pid w,
++  owner /dev/tty rw,
++
+   profile libvirt_leaseshelper {
+     include <abstractions/base>
+     /etc/libnl-3/classid r,
+-
+-    /usr/lib{,64}/libvirt/libvirt_leaseshelper m,
+     /usr/libexec/libvirt_leaseshelper m,
+-
+-    owner @{PROC}/@{pid}/net/psched r,
+-    owner @{PROC}/@{pid}/status r,
+-
++    /usr/lib{,64}/libvirt/libvirt_leaseshelper m,
++    /var/lib/libvirt/dnsmasq/*.leases rw,
++    /var/lib/libvirt/dnsmasq/*.status* rw,
++    @{run}/leaseshelper.pid rwk,
+     @{sys}/devices/system/cpu/ r,
+     @{sys}/devices/system/node/ r,
+     @{sys}/devices/system/node/*/meminfo r,
++    owner @{PROC}/@{pid}/net/psched r,
++    owner @{PROC}/@{pid}/status r,
+-    # libvirt lease and status files for dnsmasq
+-    /var/lib/libvirt/dnsmasq/*.leases  rw,
+-    /var/lib/libvirt/dnsmasq/*.status* rw,
+-
+-    @{run}/leaseshelper.pid rwk,
+   }
+-
+-  # Site-specific additions and overrides. See local/README for details.
+-  include if exists <local/usr.sbin.dnsmasq>
+ }
git clone https://git.99rst.org/PROJECT