summaryrefslogtreecommitdiff
path: root/meta-security/recipes-security/AppArmor/files/functions
blob: cef8cfe7d43dfb7551955f7933328c0d2d6c4c4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
# /lib/apparmor/functions for Debian -*- shell-script -*-
# ----------------------------------------------------------------------
#    Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
#     NOVELL (All rights reserved)
#    Copyright (c) 2008-2010 Canonical, Ltd.
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of version 2 of the GNU General Public
#    License published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, contact Novell, Inc.
# ----------------------------------------------------------------------
# Authors:
#  Kees Cook <kees@ubuntu.com>

PROFILES="/etc/apparmor.d"
PROFILES_CACHE="$PROFILES/cache"
PROFILES_VAR="/var/lib/apparmor/profiles"
PROFILES_SNAPPY="/var/lib/snapd/apparmor/profiles"
PROFILES_CACHE_VAR="/var/cache/apparmor"
PARSER="/sbin/apparmor_parser"
SECURITYFS="/sys/kernel/security"
export AA_SFS="$SECURITYFS/apparmor"

# Suppress warnings when booting in quiet mode
quiet_arg=""
[ "${QUIET:-no}" = yes ] && quiet_arg="-q"
[ "${quiet:-n}" = y ] && quiet_arg="-q"

foreach_configured_profile() {
	rc_all="0"
	for pdir in "$PROFILES" "$PROFILES_VAR" "$PROFILES_SNAPPY" ; do
		if [ ! -d "$pdir" ]; then
			continue
		fi
		num=`find "$pdir" -type f ! -name '*.md5sums' | wc -l`
		if [ "$num" = "0" ]; then
			continue
		fi

		cache_dir="$PROFILES_CACHE"
		if [ -d "$PROFILES_CACHE_VAR" ] && [ "$pdir" = "$PROFILES_VAR" ] || [ "$pdir" = "$PROFILES_SNAPPY" ]; then
			cache_dir="$PROFILES_CACHE_VAR"
		fi
		cache_args="--cache-loc=$cache_dir"
		if [ ! -d "$cache_dir" ]; then
			cache_args=
		fi

		# LP: #1383858 - expr tree simplification is too slow for
		# Touch policy on ARM, so disable it for now
		cache_extra_args=
		if [ -d "$PROFILES_CACHE_VAR" ] && [ "$pdir" = "$PROFILES_VAR" ] || [ "$pdir" = "$PROFILES_SNAPPY" ]; then
			cache_extra_args="-O no-expr-simplify"
		fi

		# If need to compile everything, then use -n1 with xargs to
		# take advantage of -P. When cache files are in use, omit -n1
		# since it is considerably faster on moderately sized profile
		# sets to give the parser all the profiles to load at once
		n1_args=
		num=`find "$cache_dir" -type f ! -name '.features' | wc -l`
		if [ "$num" = "0" ]; then
			n1_args="-n1"
		fi

		(ls -1 "$pdir" | egrep -v '(\.dpkg-(new|old|dist|bak)|~)$' | \
		while read profile; do
			if [ -f "$pdir"/"$profile" ]; then
				echo "$pdir"/"$profile"
			fi
		done) | \
		xargs $n1_args -d"\n" -P$(getconf _NPROCESSORS_ONLN) "$PARSER" "$@" $cache_args $cache_extra_args -- || {
			rc_all="$?"
			# FIXME: when the parser properly handles broken
			# profiles (LP: #1377338), remove this if statement.
			# For now, if the xargs returns with error, just run
			# through everything with -n1. (This could be broken
			# out and refactored, but this is temporary so make it
			# easy to understand and revert)
			if [ "$rc_all" != "0" ]; then
				(ls -1 "$pdir" | \
				egrep -v '(\.dpkg-(new|old|dist|bak)|~)$' | \
				while read profile; do
					if [ -f "$pdir"/"$profile" ]; then
						echo "$pdir"/"$profile"
					fi
				done) | \
				xargs -n1 -d"\n" -P$(getconf _NPROCESSORS_ONLN) "$PARSER" "$@" $cache_args $cache_extra_args -- || {
					rc_all="$?"
				}
			fi
		}
	done
	return $rc_all
}

load_configured_profiles() {
	clear_cache_if_outdated
	foreach_configured_profile $quiet_arg --write-cache --replace
}

load_configured_profiles_without_caching() {
	foreach_configured_profile $quiet_arg --replace
}

recache_profiles() {
	clear_cache
	foreach_configured_profile $quiet_arg --write-cache --skip-kernel-load
}

configured_profile_names() {
	foreach_configured_profile $quiet_arg -N 2>/dev/null | LC_COLLATE=C sort | grep -v '//'
}

running_profile_names() {
	# Output a sorted list of loaded profiles, skipping libvirt's
	# dynamically generated files
	cat "$AA_SFS"/profiles | sed -e "s/ (\(enforce\|complain\))$//" | egrep -v '^libvirt-[0-9a-f\-]+$' | LC_COLLATE=C sort | grep -v '//'
}

unload_profile() {
	echo -n "$1" > "$AA_SFS"/.remove
}

clear_cache() {
	clear_cache_system
	clear_cache_var
}

clear_cache_system() {
	find "$PROFILES_CACHE" -maxdepth 1 -type f -print0 | xargs -0 rm -f --
}

clear_cache_var() {
	find "$PROFILES_CACHE_VAR" -maxdepth 1 -type f -print0 | xargs -0 rm -f --
}

read_features_dir()
{
	for f in `ls -AU "$1"` ; do
		if [ -f "$1/$f" ] ; then
			read -r KF < "$1/$f" || true
			echo -n "$f {$KF } "
		elif [ -d "$1/$f" ] ; then
			echo -n "$f {"
			KF=`read_features_dir "$1/$f"` || true
			echo -n "$KF} "
		fi
	done
}

clear_cache_if_outdated() {
	if [ -r "$PROFILES_CACHE"/.features ]; then
		if [ -d "$AA_SFS"/features ]; then
			KERN_FEATURES=`read_features_dir "$AA_SFS"/features`
		else
			read -r KERN_FEATURES < "$AA_SFS"/features
		fi
		CACHE_FEATURES=`tr '\n' ' ' < "$PROFILES_CACHE"/.features`
		if [ "$KERN_FEATURES" != "$CACHE_FEATURES" ]; then
			clear_cache
		fi
	fi
}

unload_obsolete_profiles() {
	# Currently we must re-parse all the profiles to get policy names.  :(
	aa_configured=$(mktemp -t aa-XXXXXX)
	configured_profile_names > "$aa_configured" || true
	aa_loaded=$(mktemp -t aa-XXXXXX)
	running_profile_names > "$aa_loaded" || true
	LC_COLLATE=C comm -2 -3 "$aa_loaded" "$aa_configured" | while read profile ; do
		unload_profile "$profile"
        done
	rm -f "$aa_configured" "$aa_loaded"
}

# If the system debsum differs from the saved debsum, the new system debsum is
# saved and non-zero is returned. Returns 0 if the two debsums matched or if
# the system debsum file does not exist. This can be removed when system image
# flavors all move to snappy.
compare_and_save_debsums() {
	pkg="$1"

	if [ -n $pkg ] && [ -d "$PROFILES_VAR" ]; then
		sums="/var/lib/dpkg/info/${pkg}.md5sums"
		# store saved md5sums in /var/lib/apparmor/profiles since
		# /var/cache/apparmor might be cleared by apparmor
		saved_sums="${PROFILES_VAR}/.${pkg}.md5sums"

		if [ -f "$sums" ] && \
		   ! diff -q "$sums" "$saved_sums" 2>&1 >/dev/null ; then
			cp -f "$sums" "$saved_sums"
			return 1
		fi
	fi

	return 0
}

compare_previous_version() {
	installed="/usr/share/snappy/security-policy-version"
	previous="/var/lib/snappy/security-policy-version"

	# When just $previous doesn't exist, assume this is a new system with
	# no cache and don't do anything special.
	if [ -f "$installed" ] && [ -f "$previous" ]; then
		pv=`grep '^apparmor/' "$previous" | cut -d ' ' -f 2`
		iv=`grep '^apparmor/' "$installed" | cut -d ' ' -f 2`
		if [ -n "$iv" ] && [ -n "$pv" ] && [ "$iv" != "$pv" ]; then
			# snappy updates $previous elsewhere, so just return
			return 1
		fi
	fi

	return 0
}

# Checks to see if the current container is capable of having internal AppArmor
# profiles that should be loaded. Callers of this function should have already
# verified that they're running inside of a container environment with
# something like `systemd-detect-virt --container`.
#
# The only known container environments capable of supporting internal policy
# are LXD and LXC environment.
#
# Returns 0 if the container environment is capable of having its own internal
# policy and non-zero otherwise.
#
# IMPORTANT: This function will return 0 in the case of a non-LXD/non-LXC
# system container technology being nested inside of a LXD/LXC container that
# utilized an AppArmor namespace and profile stacking. The reason 0 will be
# returned is because .ns_stacked will be "yes" and .ns_name will still match
# "lx[dc]-*" since the nested system container technology will not have set up
# a new AppArmor profile namespace. This will result in the nested system
# container's boot process to experience failed policy loads but the boot
# process should continue without any loss of functionality. This is an
# unsupported configuration that cannot be properly handled by this function.
is_container_with_internal_policy() {
	local ns_stacked_path="${AA_SFS}/.ns_stacked"
	local ns_name_path="${AA_SFS}/.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
}