diff options
-rw-r--r-- | .SRCINFO | 16 | ||||
-rw-r--r-- | PKGBUILD | 24 | ||||
-rwxr-xr-x | follow_hook.py | 134 | ||||
-rw-r--r-- | hook_tracking_db.txt | 151 | ||||
-rw-r--r-- | relabel-selinux.hook | 12 | ||||
-rwxr-xr-x | selinux-alpm-hook | 85 |
6 files changed, 422 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 000000000000..bf905b444978 --- /dev/null +++ b/.SRCINFO @@ -0,0 +1,16 @@ +pkgbase = selinux-alpm-hook + pkgdesc = SELinux ALPM hook + pkgver = 0.1 + pkgrel = 1 + url = https://github.com/archlinuxhardened/selinux + arch = any + groups = selinux + license = GPL + depends = policycoreutils + source = relabel-selinux.hook + source = selinux-alpm-hook + sha256sums = 4df844a89a50fe1caebe0cb92fc925b3dedf7a1a8258eef53f0dd328c1cc089e + sha256sums = 983695a355aeec32bd4dc16a4586f560d730e269cef3fe9aaede82ea0245862b + +pkgname = selinux-alpm-hook + diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000000..806be4b5773c --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,24 @@ +# Maintainer: Nicolas Iooss (nicolas <dot> iooss <at> m4x <dot> org) + +pkgname=selinux-alpm-hook +pkgver=0.1 +pkgrel=1 +pkgdesc="SELinux ALPM hook" +arch=('any') +url="https://github.com/archlinuxhardened/selinux" +license=('GPL') +groups=('selinux') +depends=('policycoreutils') +source=('relabel-selinux.hook' + 'selinux-alpm-hook') +sha256sums=('4df844a89a50fe1caebe0cb92fc925b3dedf7a1a8258eef53f0dd328c1cc089e' + '983695a355aeec32bd4dc16a4586f560d730e269cef3fe9aaede82ea0245862b') + +package() { + cd "${srcdir}" + + # Prefix the hook file with "zz-" so that it is run after all other hooks + install -D -m 644 'relabel-selinux.hook' "${pkgdir}/usr/share/libalpm/hooks/zzz-relabel-selinux.hook" + + install -D -m 755 'selinux-alpm-hook' "${pkgdir}/usr/share/libalpm/scripts/selinux-alpm-hook" +} diff --git a/follow_hook.py b/follow_hook.py new file mode 100755 index 000000000000..6e15fd2ee3bd --- /dev/null +++ b/follow_hook.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +"""Follow which hook is handled by the SELinux ALPM hook""" +import logging +import re +import os.path +import subprocess +import sys + + +ALPM_HOOK_DIR = '/usr/share/libalpm/hooks/' +FOLLOW_DB = os.path.join(os.path.dirname(__file__), 'hook_tracking_db.txt') + +logger = logging.getLogger(__name__) + + +def run_pacman(args): + """Run a pacman command and return its output""" + proc = subprocess.Popen( + ['pacman'] + args, + env={'LANG': 'C'}, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = proc.stdout.read() + retval = proc.wait() + if retval: + errmsg = proc.communicate()[1].decode('ascii', errors='ignore').strip() + logger.error("pacman error %d: %s", retval, errmsg) + return + return output.decode('ascii', errors='ignore') + + +def find_package_hooks(): + """Find all available package hooks in Arch Linux repositories + + This uses the file database, which needs to be updated with "pacman -Fy" + + Yield (repo/package name, hook name) tuples + """ + alpm_hookdir_no_leading_slash = ALPM_HOOK_DIR.lstrip('/') + pkglist = run_pacman(['-Fqo', ALPM_HOOK_DIR]) or '' + for pkgname in pkglist.splitlines(): + filelist = run_pacman(['-Fql', pkgname]) or '' + for filename in filelist.splitlines(): + if filename.startswith(alpm_hookdir_no_leading_slash): + hookname = filename[len(alpm_hookdir_no_leading_slash):] + if hookname and hookname.endswith('.hook'): + yield (pkgname, hookname) + + +def get_repo_version(pkgname): + """Get the version of the package in the repositories""" + # This function can be called with a "repo/pkgname" argument + pkgname = pkgname.split('/')[-1] + pkginfo = run_pacman(['-Si', pkgname]) or '' + for line in pkginfo.splitlines(): + line = line.rstrip() + matches = re.match(r'^Version\s*:\s*(\S+)$', line, re.I) + if matches is not None: + return matches.group(1) + logger.error("Unable to find the version of package %s", pkgname) + + +def read_pkghook_db(): + """Read the package hook tracking database file""" + pkghookdb = {} + with open(FOLLOW_DB, 'r') as fdb: + for line in fdb: + line = line.strip() + if not line or line.startswith('#'): + continue + try: + pkg, ver, hook = line.split() + except ValueError: + # Ignore invalid lines + logger.warning("Invalid line in tracking database: %r", line) + continue + else: + pkghookdb[(pkg, hook)] = ver + return pkghookdb + + +def main(): + current_pkghook_db = read_pkghook_db() + everything_ok = True + for pkg, hook in find_package_hooks(): + # Get the versions in the package repositories + last_ver = get_repo_version(pkg) + if last_ver is None: + everything_ok = False + continue + # ... and in the tracking database + cur_ver = current_pkghook_db.get((pkg, hook)) + + if last_ver == cur_ver: + logger.info("OK %s %s %s", pkg, cur_ver, hook) + continue + everything_ok = False + if cur_ver is None: + logger.info("New hook:") + else: + logger.info("Upgrade needed for %s: %s -> %s", + pkg, cur_ver, last_ver) + logger.info(" DB line: %s %s %s", pkg, last_ver, hook) + hookfile = os.path.join(ALPM_HOOK_DIR, hook) + logger.info(" File: %s", hookfile) + # Try to find the Exec line in the file + if os.path.exists(hookfile): + exec_lines = [] + target_lines = [] + need_target = False + with open(hookfile, 'r') as fhook: + for line in fhook: + line = line.rstrip() + if line.startswith('Exec'): + exec_lines.append(line) + elif line.startswith('Target'): + target_lines.append(line) + elif line.startswith('NeedsTargets'): + need_target = True + # Show Target lines if NeedsTargets was found + if need_target: + for line in target_lines: + logger.info(" # %s", line) + for line in exec_lines: + logger.info(" # %s", line) + if everything_ok: + logger.info("Remember to run sudo pacman -Fy to update pacman db") + return 0 + + +if __name__ == '__main__': + logging.basicConfig( + format='[%(levelname)s] %(message)s', + level=logging.DEBUG) + sys.exit(main()) diff --git a/hook_tracking_db.txt b/hook_tracking_db.txt new file mode 100644 index 000000000000..5ad840f63041 --- /dev/null +++ b/hook_tracking_db.txt @@ -0,0 +1,151 @@ +# Track which hook is handled by SELinux ALPM hook +# Format: repo/pkgname pkgver hookname + +# Exec = /usr/bin/update-ca-trust +core/ca-certificates-utils 20170307-1 update-ca-trust.hook + +# Exec=/usr/bin/gio-querymodules /usr/lib/gio/modules +core/glib2 2.50.3-1 gio-querymodules.hook + +# Exec = /usr/bin/glib-compile-schemas /usr/share/glib-2.0/schemas +core/glib2 2.50.3-1 glib-compile-schemas.hook + +# Exec = /usr/bin/mkinitcpio -p linux +core/linux 4.10.10-1 90-linux.hook + +# Exec = /usr/bin/mkinitcpio -p linux-lts +core/linux-lts 4.9.22-1 90-linux-lts.hook + +# Exec = /usr/bin/systemd-hwdb --usr update +core/systemd 232-8 systemd-hwdb.hook + +# Target = usr/lib/sysusers.d/*.conf +# Exec = /bin/sh -c 'while read -r f; do /usr/bin/systemd-sysusers "/$f" ; done' +core/systemd 232-8 systemd-sysusers.hook + +# Target = usr/lib/tmpfiles.d/*.conf +# Exec = /bin/sh -c 'while read -r f; do /usr/bin/systemd-tmpfiles --create "/$f"; done' +core/systemd 232-8 systemd-tmpfiles.hook + +# Exec = /usr/bin/touch -c /usr +core/systemd 232-8 systemd-update.hook + +# Target = usr/share/info/* +# Exec = /bin/sh -c 'while read -r f; do install-info "$f" /usr/share/info/dir 2> /dev/null; done +core/texinfo 6.3-1 texinfo-install.hook + +# Target = usr/share/info/* +# Exec = /bin/sh -c 'while read -r f; do install-info --delete "$f" /usr/share/info/dir 2> /dev/null; done' +core/texinfo 6.3-1 texinfo-remove.hook + +# Exec = /usr/bin/appstreamcli refresh-index --force +extra/appstream 0.10.6-1 update-appstream-index.hook + +# Exec = /usr/bin/update-desktop-database --quiet +extra/desktop-file-utils 0.23-1 update-desktop-database.hook + +# Exec = /usr/lib/dkms/alpm-hook install +extra/dkms 2.3-2 70-dkms-install.hook + +# Exec = /usr/bin/perl -e "$SIG{PIPE}='DEFAULT'; exec('/usr/lib/dkms/alpm-hook', 'remove'); +extra/dkms 2.3-2 70-dkms-remove.hook + +# Exec = /usr/bin/fc-cache -s +extra/fontconfig 2.12.1-4 fontconfig.hook + +# Target = usr/share/gconf/schemas/*.schemas +# Exec = /bin/bash -c 'while read -r f; do f=$(basename "$f" .schemas); /usr/bin/gconfpkg --install $f; done' +extra/gconf 3.2.6-5 gconf-install.hook + +# Exec = /bin/bash -c 'while read -r f; do f=$(basename "$f" .schemas); /usr/bin/gconfpkg --uninstall $f; done' +extra/gconf 3.2.6-5 gconf-remove.hook + +# Exec = /usr/bin/gdk-pixbuf-query-loaders --update-cache +extra/gdk-pixbuf2 2.36.5-1 gdk-pixbuf-query-loaders.hook + +# Target = usr/share/icons/*/ +# Target = !usr/share/icons/*/?* +# Exec = /usr/share/libalpm/scripts/gtk-update-icon-cache +extra/gtk-update-icon-cache 3.22.10-1 gtk-update-icon-cache.hook + +# Exec = /usr/bin/gtk-query-immodules-2.0 --update-cache +extra/gtk2 2.24.31-1 gtk-query-immodules-2.0.hook + +# Exec = /usr/bin/gtk-query-immodules-3.0 --update-cache +extra/gtk3 3.22.10-1 gtk-query-immodules-3.0.hook + +# Exec = /bin/sh -c 'killall -q -s USR1 gvfsd || true' +extra/gvfs 1.30.3+4+gd79b4650-1 gvfsd.hook + +# Exec = /usr/bin/mkinitcpio -p linux-zen +extra/linux-zen 4.10.10-1 90-linux-zen.hook + +# Exec = /usr/bin/update-mime-database /usr/share/mime +extra/shared-mime-info 1.8-1 update-mime-database.hook + +# Exec = /usr/share/libalpm/scripts/mktexlsr +extra/texlive-bin 2016.41290-10 mktexlsr.hook + +# Exec = /usr/share/libalpm/scripts/texlive-fmtutil +extra/texlive-bin 2016.41290-10 texlive-fmtutil.hook + +# Exec = /usr/share/libalpm/scripts/texlive-updmap +extra/texlive-bin 2016.41290-10 texlive-updmap.hook + +# Exec = /usr/bin/vim -es --cmd ":helptags /usr/share/vim/vimfiles/doc" --cmd ":q" +extra/vim-runtime 8.0.0427-1 vimdoc.hook + +# Exec = /usr/lib/vlc/vlc-cache-gen -f /usr/lib/vlc/plugins +extra/vlc 2.2.4-8 update-vlc-plugin-cache.hook + +# Target = usr/share/fonts/*/ +# Target = !usr/share/fonts/encodings/* +# Exec = /usr/share/libalpm/scripts/xorg-mkfontdir +extra/xorg-mkfontdir 1.0.7-8 xorg-mkfontdir.hook + +# Exec = /usr/bin/etckeeper pre-install +community/etckeeper 1.18.6-1 05-etckeeper-pre-install.hook + +# Exec = /usr/bin/etckeeper post-install +community/etckeeper 1.18.6-1 zz-etckeeper-post-install.hook + +# Exec = /bin/bash -c "cd /usr/share/doc/ghc/html/libraries && ./gen_contents_index" +community/ghc 8.0.1-1 ghc-rebuild-doc-index.hook + +# Target = usr/share/haskell/register/*.sh +# Exec = /bin/sh -c 'while read -r f; do /bin/sh "/$f" ; done' +community/ghc 8.0.1-1 ghc-register.hook + +# Target = usr/share/haskell/unregister/*.sh +# Exec = /bin/sh -c 'while read -r f; do /bin/sh "/$f" ; done' +community/ghc 8.0.1-1 ghc-unregister.hook + +# Exec = /usr/bin/mkinitcpio -p linux-grsec +community/linux-grsec 1:4.9.22.r201704120836-1 99-linux-grsec.hook + +# Exec = /usr/share/libalpm/scripts/snap-pac pre +community/snap-pac 1.1-1 00_snapper-pre.hook + +# Exec = /usr/bin/bash -c "rm -f /tmp/snap-pac-pre_*" +community/snap-pac 1.1-1 10_snap-pac-removal.hook + +# Exec = /usr/share/libalpm/scripts/snap-pac post +community/snap-pac 1.1-1 zy_snapper-post.hook + +# Exec = /usr/bin/true +community/snap-pac 1.1-1 zz_snap-pac-install.hook + +# Exec = /bin/cp -f /etc/trusted-key.key /etc/unbound/ +community/unbound 1.6.1-3 unbound-key.hook + +# Exec = /usr/bin/fc-cache-32 -s +multilib/lib32-fontconfig 2.12.1-4 lib32-fontconfig.hook + +# Exec = /usr/bin/gdk-pixbuf-query-loaders-32 --update-cache +multilib/lib32-gdk-pixbuf2 2.36.2-2 gdk-pixbuf-query-loaders-32.hook + +# Exec = /usr/bin/gio-querymodules-32 /usr/lib32/gio/module +multilib/lib32-glib2 2.50.1-2 gio-querymodules-32.hook + +# Exec = /bin/sh -c "GTK_PATH=/usr/lib32/gtk-3.0 /usr/bin/gtk-query-immodules-3.0-32 --update-cache" +multilib/lib32-gtk3 3.22.7-1 gtk-query-immodules-3.0-32.hook diff --git a/relabel-selinux.hook b/relabel-selinux.hook new file mode 100644 index 000000000000..451a23aaf6ab --- /dev/null +++ b/relabel-selinux.hook @@ -0,0 +1,12 @@ +[Trigger] +Operation = Install +Operation = Upgrade +Type = File +Target = * + +[Action] +Description = SELinux: relabel installed files +Depends = policycoreutils +When = PostTransaction +Exec = /usr/share/libalpm/scripts/selinux-alpm-hook +NeedsTargets diff --git a/selinux-alpm-hook b/selinux-alpm-hook new file mode 100755 index 000000000000..3d7ac26d1dfa --- /dev/null +++ b/selinux-alpm-hook @@ -0,0 +1,85 @@ +#!/bin/bash +# SELinux ALPM hook +# Relabel installed files after an install or an update + +# Define a function to print an error mesage and exit +die() { + echo >&2 "$@" + exit 1 +} + +# Verify that the hook is running with / as current working directory +if [ "$(pwd)" != '/' ]; then + die 'Hook is being executed outside of root directory. Aborting.' +fi + +# Do not do anything if SELinux is disabled +if sestatus | grep '^SELinux status:\s*disabled' > /dev/null; then + exit 0 +fi + +# Read package files from stdin, as restorecon may error out on any non-existing file +echo 'Relabeling package files...' +while read -r FILE; do + if [ -e "$FILE" ] || [ -L "$FILE" ] ; then + /usr/bin/restorecon -F "$FILE" || die "Error while relabeling $FILE" + else + # Only show a warning when a file does not exist as this is not fatal + echo "Ugh, an installed file does not exist: $FILE" + fi +done + +# The install hooks of packages create files which got labelled with the wrong SELinux user +# (e.g. sysadm_u instead of system_u). Relabel all these files too. +# Check the order of this list with: sed -n '/^GEN_DIRS=/,/^)/{/^ /p}' |LANG=C sort -c +echo 'Relabeling generated directories...' +GEN_DIRS=( + '/boot/' # linux:99-linux.hook + '/etc/ca-certificates/extracted/' # ca-certificates-utils:update-ca-trust.hook + '/etc/gconf/' # gconf:gconf-install.hook + '/etc/ld.so.cache' # glibc install: ldconfig -r . + '/etc/pacman.d/gnupg/' # archlinux-keyring install: pacman-key --populate archlinux + '/etc/ssl/certs/' # ca-certificates-utils:update-ca-trust.hook + '/etc/texmf/ls-R' # texlive-bin install: mktexlsr + '/etc/udev/hwdb.bin' # systemd:udev-hwdb.hook + '/etc/unbound/trusted-key.key' # unbound:unbound-key.hook + '/usr/lib/gdk-pixbuf-*/*/loaders.cache' # gdk-pixbuf2:gdk-pixbuf-query-loaders.hook + '/usr/lib/ghc-*/package.conf.d/' # ghc:ghc-register.hook, ghc:ghc-unregister.hook + '/usr/lib/gio/modules/' # glib2:gio-querymodules.hook + '/usr/lib/graphviz/' # graphviz install: dot -c + '/usr/lib/gtk-2.0/' # gtk2:gtk-query-immodules-2.0.hook + '/usr/lib/gtk-3.0/' # gtk3:gtk-query-immodules-3.0.hook + '/usr/lib/locale/locale-archive' # glibc install: locale-gen + '/usr/lib/modules/' # dkms:70-dkms-install.hook + '/usr/lib/vlc/plugins/plugins.dat' # vlc:update-vlc-plugin-cache.hook + '/usr/lib32/gdk-pixbuf-*/*/loaders.cache' # lib32-gdk-pixbuf2 install: gdk-pixbuf-query-loaders-32 --update-cache + '/usr/lib32/gio/modules/' # lib32-glib2:gio-querymodules-32.hook + '/usr/lib32/gtk-3.0/' # lib32-gtk3:gtk-query-immodules-3.0-32.hook + '/usr/share/.mono/certs' # mono install: cert-sync /etc/ssl/certs/ca-certificates.crt + '/usr/share/applications/mimeinfo.cache' # desktop-file-utils:update-desktop-database.hook + '/usr/share/doc/ghc/html/libraries/' # ghc:ghc-rebuild-doc-index.hook + '/usr/share/fonts/' # xorg-mkfontdir:xorg-mkfontdir.hook + '/usr/share/glib-2.0/schemas/' # glib2:glib-compile-schemes.hook + '/usr/share/icons/' # gtk-update-icon-cache:gtk-update-icon-cache.hook + '/usr/share/info/dir' # texinfo:texinfo-install.hook + '/usr/share/mime/' # shared-mime-info:update-mime-database.hook + '/usr/share/texmf*/ls-R' # texlive-bin:mktexlsr.hook + '/usr/share/vim/vimfiles/doc/tags' # vim-runtime:vimdoc.hook + '/var/cache/fontconfig/' # fontconfig:fontconfig.hook + '/var/cache/ldconfig/' # glibc install: ldconfig -r . + '/var/cache/man/' # man-db timer + '/var/cache/pacman/' # pacman + '/var/lib/dkms/' # dkms:70-dkms-install.hook + '/var/lib/pacman/' # pacman + '/var/lib/systemd/catalog/database' # systemd install: journalctl --update-catalog + '/var/lib/texmf/' # texlive-bin:mktexlsr.hook +) + +# Do NOT put quotes around the array, so that wildcards get expanded +# shellcheck disable=SC2068 +for DIR in ${GEN_DIRS[@]}; do + # Only relabel directories which exist + if [ -e "$DIR" ]; then + /usr/bin/restorecon -RF "$DIR" || die "Error while relabeling generated directories" + fi +done |