--- /dev/null
+# 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))
--- /dev/null
+#!/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
+}
--- /dev/null
+#!/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
+}
--- /dev/null
+--- /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
--- /dev/null
+--- 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()
--- /dev/null
+--- 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:
--- /dev/null
+--- 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"
+
--- /dev/null
+--- 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
--- /dev/null
+--- 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>
+ }