blob: e9e2bbfbf0eb5289eef4c0a4b855d2cfa359207c [file] [log] [blame]
Richard Marian Thomaiyar14fddef2018-07-13 23:55:56 +05301# /lib/apparmor/functions for Debian -*- shell-script -*-
2# ----------------------------------------------------------------------
3# Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
4# NOVELL (All rights reserved)
5# Copyright (c) 2008-2010 Canonical, Ltd.
6#
7# This program is free software; you can redistribute it and/or
8# modify it under the terms of version 2 of the GNU General Public
9# License published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, contact Novell, Inc.
18# ----------------------------------------------------------------------
19# Authors:
20# Kees Cook <kees@ubuntu.com>
21
22PROFILES="/etc/apparmor.d"
23PROFILES_CACHE="$PROFILES/cache"
24PROFILES_VAR="/var/lib/apparmor/profiles"
25PROFILES_SNAPPY="/var/lib/snapd/apparmor/profiles"
26PROFILES_CACHE_VAR="/var/cache/apparmor"
27PARSER="/sbin/apparmor_parser"
28SECURITYFS="/sys/kernel/security"
29export AA_SFS="$SECURITYFS/apparmor"
30
31# Suppress warnings when booting in quiet mode
32quiet_arg=""
33[ "${QUIET:-no}" = yes ] && quiet_arg="-q"
34[ "${quiet:-n}" = y ] && quiet_arg="-q"
35
36foreach_configured_profile() {
37 rc_all="0"
38 for pdir in "$PROFILES" "$PROFILES_VAR" "$PROFILES_SNAPPY" ; do
39 if [ ! -d "$pdir" ]; then
40 continue
41 fi
42 num=`find "$pdir" -type f ! -name '*.md5sums' | wc -l`
43 if [ "$num" = "0" ]; then
44 continue
45 fi
46
47 cache_dir="$PROFILES_CACHE"
48 if [ -d "$PROFILES_CACHE_VAR" ] && [ "$pdir" = "$PROFILES_VAR" ] || [ "$pdir" = "$PROFILES_SNAPPY" ]; then
49 cache_dir="$PROFILES_CACHE_VAR"
50 fi
51 cache_args="--cache-loc=$cache_dir"
52 if [ ! -d "$cache_dir" ]; then
53 cache_args=
54 fi
55
56 # LP: #1383858 - expr tree simplification is too slow for
57 # Touch policy on ARM, so disable it for now
58 cache_extra_args=
59 if [ -d "$PROFILES_CACHE_VAR" ] && [ "$pdir" = "$PROFILES_VAR" ] || [ "$pdir" = "$PROFILES_SNAPPY" ]; then
60 cache_extra_args="-O no-expr-simplify"
61 fi
62
63 # If need to compile everything, then use -n1 with xargs to
64 # take advantage of -P. When cache files are in use, omit -n1
65 # since it is considerably faster on moderately sized profile
66 # sets to give the parser all the profiles to load at once
67 n1_args=
68 num=`find "$cache_dir" -type f ! -name '.features' | wc -l`
69 if [ "$num" = "0" ]; then
70 n1_args="-n1"
71 fi
72
73 (ls -1 "$pdir" | egrep -v '(\.dpkg-(new|old|dist|bak)|~)$' | \
74 while read profile; do
75 if [ -f "$pdir"/"$profile" ]; then
76 echo "$pdir"/"$profile"
77 fi
78 done) | \
79 xargs $n1_args -d"\n" -P$(getconf _NPROCESSORS_ONLN) "$PARSER" "$@" $cache_args $cache_extra_args -- || {
80 rc_all="$?"
81 # FIXME: when the parser properly handles broken
82 # profiles (LP: #1377338), remove this if statement.
83 # For now, if the xargs returns with error, just run
84 # through everything with -n1. (This could be broken
85 # out and refactored, but this is temporary so make it
86 # easy to understand and revert)
87 if [ "$rc_all" != "0" ]; then
88 (ls -1 "$pdir" | \
89 egrep -v '(\.dpkg-(new|old|dist|bak)|~)$' | \
90 while read profile; do
91 if [ -f "$pdir"/"$profile" ]; then
92 echo "$pdir"/"$profile"
93 fi
94 done) | \
95 xargs -n1 -d"\n" -P$(getconf _NPROCESSORS_ONLN) "$PARSER" "$@" $cache_args $cache_extra_args -- || {
96 rc_all="$?"
97 }
98 fi
99 }
100 done
101 return $rc_all
102}
103
104load_configured_profiles() {
105 clear_cache_if_outdated
106 foreach_configured_profile $quiet_arg --write-cache --replace
107}
108
109load_configured_profiles_without_caching() {
110 foreach_configured_profile $quiet_arg --replace
111}
112
113recache_profiles() {
114 clear_cache
115 foreach_configured_profile $quiet_arg --write-cache --skip-kernel-load
116}
117
118configured_profile_names() {
119 foreach_configured_profile $quiet_arg -N 2>/dev/null | LC_COLLATE=C sort | grep -v '//'
120}
121
122running_profile_names() {
123 # Output a sorted list of loaded profiles, skipping libvirt's
124 # dynamically generated files
125 cat "$AA_SFS"/profiles | sed -e "s/ (\(enforce\|complain\))$//" | egrep -v '^libvirt-[0-9a-f\-]+$' | LC_COLLATE=C sort | grep -v '//'
126}
127
128unload_profile() {
129 echo -n "$1" > "$AA_SFS"/.remove
130}
131
132clear_cache() {
133 clear_cache_system
134 clear_cache_var
135}
136
137clear_cache_system() {
138 find "$PROFILES_CACHE" -maxdepth 1 -type f -print0 | xargs -0 rm -f --
139}
140
141clear_cache_var() {
142 find "$PROFILES_CACHE_VAR" -maxdepth 1 -type f -print0 | xargs -0 rm -f --
143}
144
145read_features_dir()
146{
Andrew Geisslerd1d22e62020-10-16 10:14:32 -0500147 for f in `ls -A "$1"` ; do
Richard Marian Thomaiyar14fddef2018-07-13 23:55:56 +0530148 if [ -f "$1/$f" ] ; then
149 read -r KF < "$1/$f" || true
150 echo -n "$f {$KF } "
151 elif [ -d "$1/$f" ] ; then
152 echo -n "$f {"
153 KF=`read_features_dir "$1/$f"` || true
154 echo -n "$KF} "
155 fi
156 done
157}
158
159clear_cache_if_outdated() {
160 if [ -r "$PROFILES_CACHE"/.features ]; then
161 if [ -d "$AA_SFS"/features ]; then
162 KERN_FEATURES=`read_features_dir "$AA_SFS"/features`
163 else
164 read -r KERN_FEATURES < "$AA_SFS"/features
165 fi
166 CACHE_FEATURES=`tr '\n' ' ' < "$PROFILES_CACHE"/.features`
167 if [ "$KERN_FEATURES" != "$CACHE_FEATURES" ]; then
168 clear_cache
169 fi
170 fi
171}
172
173unload_obsolete_profiles() {
174 # Currently we must re-parse all the profiles to get policy names. :(
175 aa_configured=$(mktemp -t aa-XXXXXX)
176 configured_profile_names > "$aa_configured" || true
177 aa_loaded=$(mktemp -t aa-XXXXXX)
178 running_profile_names > "$aa_loaded" || true
179 LC_COLLATE=C comm -2 -3 "$aa_loaded" "$aa_configured" | while read profile ; do
180 unload_profile "$profile"
181 done
182 rm -f "$aa_configured" "$aa_loaded"
183}
184
185# If the system debsum differs from the saved debsum, the new system debsum is
186# saved and non-zero is returned. Returns 0 if the two debsums matched or if
187# the system debsum file does not exist. This can be removed when system image
188# flavors all move to snappy.
189compare_and_save_debsums() {
190 pkg="$1"
191
192 if [ -n $pkg ] && [ -d "$PROFILES_VAR" ]; then
193 sums="/var/lib/dpkg/info/${pkg}.md5sums"
194 # store saved md5sums in /var/lib/apparmor/profiles since
195 # /var/cache/apparmor might be cleared by apparmor
196 saved_sums="${PROFILES_VAR}/.${pkg}.md5sums"
197
198 if [ -f "$sums" ] && \
199 ! diff -q "$sums" "$saved_sums" 2>&1 >/dev/null ; then
200 cp -f "$sums" "$saved_sums"
201 return 1
202 fi
203 fi
204
205 return 0
206}
207
208compare_previous_version() {
209 installed="/usr/share/snappy/security-policy-version"
210 previous="/var/lib/snappy/security-policy-version"
211
212 # When just $previous doesn't exist, assume this is a new system with
213 # no cache and don't do anything special.
214 if [ -f "$installed" ] && [ -f "$previous" ]; then
215 pv=`grep '^apparmor/' "$previous" | cut -d ' ' -f 2`
216 iv=`grep '^apparmor/' "$installed" | cut -d ' ' -f 2`
217 if [ -n "$iv" ] && [ -n "$pv" ] && [ "$iv" != "$pv" ]; then
218 # snappy updates $previous elsewhere, so just return
219 return 1
220 fi
221 fi
222
223 return 0
224}
225
226# Checks to see if the current container is capable of having internal AppArmor
227# profiles that should be loaded. Callers of this function should have already
228# verified that they're running inside of a container environment with
229# something like `systemd-detect-virt --container`.
230#
231# The only known container environments capable of supporting internal policy
232# are LXD and LXC environment.
233#
234# Returns 0 if the container environment is capable of having its own internal
235# policy and non-zero otherwise.
236#
237# IMPORTANT: This function will return 0 in the case of a non-LXD/non-LXC
238# system container technology being nested inside of a LXD/LXC container that
239# utilized an AppArmor namespace and profile stacking. The reason 0 will be
240# returned is because .ns_stacked will be "yes" and .ns_name will still match
241# "lx[dc]-*" since the nested system container technology will not have set up
242# a new AppArmor profile namespace. This will result in the nested system
243# container's boot process to experience failed policy loads but the boot
244# process should continue without any loss of functionality. This is an
245# unsupported configuration that cannot be properly handled by this function.
246is_container_with_internal_policy() {
247 local ns_stacked_path="${AA_SFS}/.ns_stacked"
248 local ns_name_path="${AA_SFS}/.ns_name"
249 local ns_stacked
250 local ns_name
251
252 if ! [ -f "$ns_stacked_path" ] || ! [ -f "$ns_name_path" ]; then
253 return 1
254 fi
255
256 read -r ns_stacked < "$ns_stacked_path"
257 if [ "$ns_stacked" != "yes" ]; then
258 return 1
259 fi
260
261 # LXD and LXC set up AppArmor namespaces starting with "lxd-" and
262 # "lxc-", respectively. Return non-zero for all other namespace
263 # identifiers.
264 read -r ns_name < "$ns_name_path"
265 if [ "${ns_name#lxd-*}" = "$ns_name" ] && \
266 [ "${ns_name#lxc-*}" = "$ns_name" ]; then
267 return 1
268 fi
269
270 return 0
271}