rc.apparmor.functions 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #!/bin/sh
  2. # ----------------------------------------------------------------------
  3. # Copyright (c) 1999-2008 NOVELL (All rights reserved)
  4. # Copyright (c) 2009-2018 Canonical Ltd. (All rights reserved)
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of version 2 of the GNU General Public
  8. # License published by the Free Software Foundation.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, contact Novell, Inc.
  17. # ----------------------------------------------------------------------
  18. # rc.apparmor.functions by Steve Beattie
  19. #
  20. # NOTE: rc.apparmor initscripts that source this file need to implement
  21. # the following set of functions:
  22. # aa_action
  23. # aa_log_action_start
  24. # aa_log_action_end
  25. # aa_log_success_msg
  26. # aa_log_warning_msg
  27. # aa_log_failure_msg
  28. # aa_log_skipped_msg
  29. # aa_log_daemon_msg
  30. # aa_log_end_msg
  31. # Some nice defines that we use
  32. MODULE=apparmor
  33. PARSER=/sbin/apparmor_parser
  34. PARSER_OPTS=--write-cache
  35. # Suppress warnings when booting in quiet mode
  36. if [ "${QUIET:-no}" = yes ] || [ "${quiet:-n}" = y ]; then
  37. PARSER_OPTS="$PARSER_OPTS --quiet"
  38. fi
  39. if [ -d /etc/apparmor.d ] ; then
  40. PROFILE_DIRS=/etc/apparmor.d
  41. else
  42. aa_log_warning_msg "Unable to find profiles directory, installation problem?"
  43. fi
  44. ADDITIONAL_PROFILE_DIR=
  45. if [ -n "$ADDITIONAL_PROFILE_DIR" ] && [ -d "$ADDITIONAL_PROFILE_DIR" ]; then
  46. PROFILE_DIRS="${PROFILE_DIRS} ${ADDITIONAL_PROFILE_DIR}"
  47. fi
  48. AA_STATUS=/usr/sbin/aa-status
  49. SECURITYFS=/sys/kernel/security
  50. # keep exit status from parser during profile load. 0 is good, 1 is bad
  51. STATUS=0
  52. # Test if the apparmor "module" is present.
  53. is_apparmor_present() {
  54. [ -d /sys/module/apparmor ]
  55. }
  56. # Checks to see if the current container is capable of having internal AppArmor
  57. # profiles that should be loaded. Callers of this function should have already
  58. # verified that they're running inside of a container environment with
  59. # something like `systemd-detect-virt --container`.
  60. #
  61. # The only known container environments capable of supporting internal policy
  62. # are LXD and LXC environment.
  63. #
  64. # Returns 0 if the container environment is capable of having its own internal
  65. # policy and non-zero otherwise.
  66. #
  67. # IMPORTANT: This function will return 0 in the case of a non-LXD/non-LXC
  68. # system container technology being nested inside of a LXD/LXC container that
  69. # utilized an AppArmor namespace and profile stacking. The reason 0 will be
  70. # returned is because .ns_stacked will be "yes" and .ns_name will still match
  71. # "lx[dc]-*" since the nested system container technology will not have set up
  72. # a new AppArmor profile namespace. This will result in the nested system
  73. # container's boot process to experience failed policy loads but the boot
  74. # process should continue without any loss of functionality. This is an
  75. # unsupported configuration that cannot be properly handled by this function.
  76. is_container_with_internal_policy() {
  77. # this function is sometimes called independently of
  78. # is_apparmor_loaded(), so define this here.
  79. SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}"
  80. local ns_stacked_path="${SFS_MOUNTPOINT}/.ns_stacked"
  81. local ns_name_path="${SFS_MOUNTPOINT}/.ns_name"
  82. local ns_stacked
  83. local ns_name
  84. if ! [ -f "$ns_stacked_path" ] || ! [ -f "$ns_name_path" ]; then
  85. return 1
  86. fi
  87. read -r ns_stacked < "$ns_stacked_path"
  88. if [ "$ns_stacked" != "yes" ]; then
  89. return 1
  90. fi
  91. # LXD and LXC set up AppArmor namespaces starting with "lxd-" and
  92. # "lxc-", respectively. Return non-zero for all other namespace
  93. # identifiers.
  94. read -r ns_name < "$ns_name_path"
  95. if [ "${ns_name#lxd-*}" = "$ns_name" ] && \
  96. [ "${ns_name#lxc-*}" = "$ns_name" ]; then
  97. return 1
  98. fi
  99. return 0
  100. }
  101. # This set of patterns to skip needs to be kept in sync with
  102. # AppArmor.pm::isSkippableFile()
  103. # returns 0 if profile should NOT be skipped
  104. # returns 1 on verbose skip
  105. # returns 2 on silent skip
  106. skip_profile() {
  107. local profile=$1
  108. if [ "${profile%.rpmnew}" != "${profile}" -o \
  109. "${profile%.rpmsave}" != "${profile}" -o \
  110. "${profile%.orig}" != "${profile}" -o \
  111. "${profile%.rej}" != "${profile}" -o \
  112. "${profile%\~}" != "${profile}" ] ; then
  113. return 1
  114. fi
  115. # Silently ignore the dpkg, pacman, and xbps files
  116. if [ "${profile%.dpkg-new}" != "${profile}" -o \
  117. "${profile%.dpkg-old}" != "${profile}" -o \
  118. "${profile%.dpkg-dist}" != "${profile}" -o \
  119. "${profile%.dpkg-bak}" != "${profile}" -o \
  120. "${profile%.dpkg-remove}" != "${profile}" -o \
  121. "${profile%.pacsave}" != "${profile}" -o \
  122. "${profile%.pacnew}" != "${profile}" ] ; then
  123. return 2
  124. fi
  125. if echo "${profile}" | egrep -q '^.+\.new-[0-9\.]+_[0-9]+$'; then
  126. return 2
  127. fi
  128. return 0
  129. }
  130. __parse_profiles_dir() {
  131. local parser_cmd="$1"
  132. local profile_dir="$2"
  133. local status=0
  134. if [ ! -d "$profile_dir" ]; then
  135. aa_log_failure_msg "Profile directory not found: $profile_dir"
  136. return 1
  137. fi
  138. if [ -z "$(ls $profile_dir/)" ]; then
  139. aa_log_failure_msg "No profiles found in $profile_dir"
  140. return 1
  141. fi
  142. # Note: the parser automatically skips files that match skip_profile()
  143. # when we pass it a directory, but not when we pass it an individual
  144. # profile. So we need to use skip_profile only in the latter case,
  145. # as long as the parser is in sync' with skip_profile().
  146. "$PARSER" $PARSER_OPTS $parser_cmd -- "$profile_dir" || {
  147. # FIXME: once the parser properly handles broken profiles
  148. # (LP: #1377338), remove the following code and the
  149. # skip_profile() function. For now, if the parser returns
  150. # an error, just run it again separately on each profile.
  151. for profile in $profile_dir/*; do
  152. skip_profile "${profile}"
  153. skip=$?
  154. if [ "$skip" -eq 2 ]; then
  155. # Ignore skip status == 2 (silent skip)
  156. continue
  157. elif [ "$skip" -ne 0 ] ; then
  158. aa_log_skipped_msg "$profile"
  159. logger -t "AppArmor(init)" -p daemon.warn \
  160. "Skipping profile $profile"
  161. continue
  162. fi
  163. if [ ! -f "${profile}" ] ; then
  164. continue
  165. fi
  166. echo "$profile"
  167. done | \
  168. # Use xargs to parallelize calls to the parser over all CPUs
  169. xargs -n1 -d"\n" --max-procs=$(getconf _NPROCESSORS_ONLN) \
  170. "$PARSER" $PARSER_OPTS $parser_cmd --
  171. if [ $? -ne 0 ]; then
  172. status=1
  173. aa_log_failure_msg "At least one profile failed to load"
  174. fi
  175. }
  176. return $status
  177. }
  178. parse_profiles() {
  179. # get parser arg
  180. case "$1" in
  181. load)
  182. PARSER_CMD="--add"
  183. PARSER_MSG="Loading AppArmor profiles "
  184. ;;
  185. reload)
  186. PARSER_CMD="--replace"
  187. PARSER_MSG="Reloading AppArmor profiles "
  188. ;;
  189. *)
  190. aa_log_failure_msg "required 'load' or 'reload'"
  191. exit 1
  192. ;;
  193. esac
  194. aa_log_action_start "$PARSER_MSG"
  195. # run the parser on all of the apparmor profiles
  196. if [ ! -f "$PARSER" ]; then
  197. aa_log_failure_msg "AppArmor parser not found"
  198. aa_log_action_end 1
  199. exit 1
  200. fi
  201. for profile_dir in $PROFILE_DIRS; do
  202. __parse_profiles_dir "$PARSER_CMD" "$profile_dir" || STATUS=$?
  203. done
  204. aa_log_action_end "$STATUS"
  205. return $STATUS
  206. }
  207. profiles_names_list() {
  208. # run the parser on all of the apparmor profiles
  209. if [ ! -f "$PARSER" ]; then
  210. aa_log_failure_msg "- AppArmor parser not found"
  211. exit 1
  212. fi
  213. for profile_dir in $PROFILE_DIRS; do
  214. if [ ! -d "$profile_dir" ]; then
  215. aa_log_warning_msg "- Profile directory not found: $profile_dir"
  216. continue
  217. fi
  218. for profile in $profile_dir/*; do
  219. if skip_profile "${profile}" && [ -f "${profile}" ] ; then
  220. LIST_ADD=$($PARSER -N "$profile" )
  221. if [ $? -eq 0 ]; then
  222. echo "$LIST_ADD"
  223. fi
  224. fi
  225. done
  226. done
  227. }
  228. failstop_system() {
  229. level=$(runlevel | cut -d" " -f2)
  230. if [ $level -ne "1" ] ; then
  231. aa_log_failure_msg "- could not start AppArmor. Changing to runlevel 1"
  232. telinit 1;
  233. return -1;
  234. fi
  235. aa_log_failure_msg "- could not start AppArmor."
  236. return -1
  237. }
  238. is_apparmor_loaded() {
  239. if ! is_securityfs_mounted ; then
  240. mount_securityfs
  241. fi
  242. if [ -f "${SECURITYFS}/${MODULE}/profiles" ]; then
  243. SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}"
  244. return 0
  245. fi
  246. is_apparmor_present
  247. return $?
  248. }
  249. is_securityfs_mounted() {
  250. test -d ${SECURITYFS} -a -d /sys/fs/cgroup/systemd || grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts
  251. return $?
  252. }
  253. mount_securityfs() {
  254. if grep -q securityfs /proc/filesystems ; then
  255. aa_action "Mounting securityfs on ${SECURITYFS}" \
  256. mount -t securityfs securityfs "${SECURITYFS}"
  257. return $?
  258. fi
  259. return 0
  260. }
  261. apparmor_start() {
  262. aa_log_daemon_msg "Starting AppArmor"
  263. if ! is_apparmor_present ; then
  264. aa_log_failure_msg "Starting AppArmor - failed, To enable AppArmor, ensure your kernel is configured with CONFIG_SECURITY_APPARMOR=y then add 'security=apparmor apparmor=1' to the kernel command line"
  265. aa_log_end_msg 1
  266. return 1
  267. elif ! is_apparmor_loaded ; then
  268. aa_log_failure_msg "Starting AppArmor - AppArmor control files aren't available under /sys/kernel/security/, please make sure securityfs is mounted."
  269. aa_log_end_msg 1
  270. return 1
  271. fi
  272. if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
  273. aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
  274. aa_log_end_msg 1
  275. return 1
  276. fi
  277. # if there is anything in the profiles file don't load
  278. if ! read line < "$SFS_MOUNTPOINT/profiles"; then
  279. parse_profiles load
  280. else
  281. aa_log_skipped_msg ": already loaded with profiles."
  282. return 0
  283. fi
  284. aa_log_end_msg 0
  285. return 0
  286. }
  287. remove_profiles() {
  288. # removing profiles as we directly read from apparmorfs
  289. # doesn't work, since we are removing entries which screws up
  290. # our position. Lets hope there are never enough profiles to
  291. # overflow the variable
  292. if ! is_apparmor_loaded ; then
  293. aa_log_failure_msg "AppArmor module is not loaded"
  294. return 1
  295. fi
  296. if [ ! -w "$SFS_MOUNTPOINT/.remove" ] ; then
  297. aa_log_failure_msg "Root privileges not available"
  298. return 1
  299. fi
  300. if [ ! -x "${PARSER}" ] ; then
  301. aa_log_failure_msg "Unable to execute AppArmor parser"
  302. return 1
  303. fi
  304. retval=0
  305. # We filter child profiles as removing the parent will remove
  306. # the children
  307. sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
  308. LC_COLLATE=C sort | grep -v // | {
  309. while read profile ; do
  310. echo -n "$profile" > "$SFS_MOUNTPOINT/.remove"
  311. rc=$?
  312. if [ ${rc} -ne 0 ] ; then
  313. retval=${rc}
  314. fi
  315. done
  316. return ${retval}
  317. }
  318. }
  319. apparmor_stop() {
  320. aa_log_daemon_msg "Unloading AppArmor profiles "
  321. remove_profiles
  322. rc=$?
  323. aa_log_end_msg $rc
  324. return $rc
  325. }
  326. apparmor_kill() {
  327. aa_log_daemon_msg "Unloading AppArmor modules "
  328. if ! is_apparmor_loaded ; then
  329. aa_log_failure_msg "AppArmor module is not loaded"
  330. return 1
  331. fi
  332. if is_apparmor_present ; then
  333. MODULE=apparmor
  334. else
  335. aa_log_failure_msg "AppArmor is builtin"
  336. return 1
  337. fi
  338. /sbin/modprobe -qr $MODULE
  339. rc=$?
  340. aa_log_end_msg $rc
  341. return $rc
  342. }
  343. __apparmor_restart() {
  344. if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then
  345. aa_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?"
  346. return 4
  347. fi
  348. aa_log_daemon_msg "Restarting AppArmor"
  349. parse_profiles reload
  350. rc=$?
  351. aa_log_end_msg $rc
  352. return $rc
  353. }
  354. apparmor_restart() {
  355. if ! is_apparmor_loaded ; then
  356. apparmor_start
  357. rc=$?
  358. return $rc
  359. fi
  360. __apparmor_restart
  361. return $?
  362. }
  363. apparmor_try_restart() {
  364. if ! is_apparmor_loaded ; then
  365. return 0
  366. fi
  367. __apparmor_restart
  368. return $?
  369. }
  370. apparmor_status () {
  371. if test -x ${AA_STATUS} ; then
  372. ${AA_STATUS} --verbose
  373. return $?
  374. fi
  375. if ! is_apparmor_loaded ; then
  376. echo "AppArmor is not loaded."
  377. rc=1
  378. else
  379. echo "AppArmor is enabled."
  380. rc=0
  381. fi
  382. echo "Install the apparmor-utils package to receive more detailed"
  383. echo "status information here (or examine ${SFS_MOUNTPOINT} directly)."
  384. return $rc
  385. }