diff --git a/trunk/novell4gentoo/sec-policy/apparmor-profiles/ChangeLog b/trunk/novell4gentoo/sec-policy/apparmor-profiles/ChangeLog new file mode 100644 index 0000000..297df4b --- /dev/null +++ b/trunk/novell4gentoo/sec-policy/apparmor-profiles/ChangeLog @@ -0,0 +1,10 @@ +# ChangeLog for sec-policy/apparmor-profiles +# Copyright 1999-2006 Gentoo Foundation; Distributed under the GPL v2 +# $Header: $ + + 13 Aug 2006; Mario Fetka + +apparmor-profiles-2.0_p6376.ebuild: + Initial Import of + Matthew Snelham + Apparmor ebuilds + diff --git a/trunk/novell4gentoo/sec-policy/apparmor-profiles/Manifest b/trunk/novell4gentoo/sec-policy/apparmor-profiles/Manifest new file mode 100644 index 0000000..8ddd47f --- /dev/null +++ b/trunk/novell4gentoo/sec-policy/apparmor-profiles/Manifest @@ -0,0 +1,8 @@ +DIST apparmor-profiles-2.0-6376.tar.gz 33651 RMD160 cd64269b9c12fd60256f624b7b81aa1bddbb74c3 SHA1 151a18c3bc05355f24188cd1bc4b77cbae7e15dd SHA256 55103b0ce98616b6860fb43eff23587ad0faf5e38ab828bdd025d87a55b0f073 +EBUILD apparmor-profiles-2.0_p6376.ebuild 1761 RMD160 c71682fbb885b3be54813c691258d4c6a3d6de59 SHA1 778fa06e704b167e03946a0a26f2b6e5f282f37f SHA256 350943388432d8280888d86ea36e3fb92af128c64553ef99f3421c07b2274d04 +MD5 84869f70eb14cb24d0d296fa2f764cef apparmor-profiles-2.0_p6376.ebuild 1761 +RMD160 c71682fbb885b3be54813c691258d4c6a3d6de59 apparmor-profiles-2.0_p6376.ebuild 1761 +SHA256 350943388432d8280888d86ea36e3fb92af128c64553ef99f3421c07b2274d04 apparmor-profiles-2.0_p6376.ebuild 1761 +MD5 45e145b77a88f384033973676b4cf1c0 files/digest-apparmor-profiles-2.0_p6376 277 +RMD160 75b6c9ba5340bafa72a8410339ceeeda13a71d00 files/digest-apparmor-profiles-2.0_p6376 277 +SHA256 b4229927d9665b3c60a9ba4d956d942083d5d7c59b8f3ced3c36d8741efad10d files/digest-apparmor-profiles-2.0_p6376 277 diff --git a/trunk/novell4gentoo/sec-policy/apparmor-profiles/apparmor-profiles-2.0_p6376.ebuild b/trunk/novell4gentoo/sec-policy/apparmor-profiles/apparmor-profiles-2.0_p6376.ebuild new file mode 100644 index 0000000..6b5b188 --- /dev/null +++ b/trunk/novell4gentoo/sec-policy/apparmor-profiles/apparmor-profiles-2.0_p6376.ebuild @@ -0,0 +1,58 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +inherit eutils + +MY_P="${P/_p/-}" +MY_S="${WORKDIR}/${P/_p*//}" +DESCRIPTION="AppArmor pre-built application encapsulation profiles." +HOMEPAGE="http://forge.novell.com/modules/xfmod/project/?apparmor" +SRC_URI="http://forge.novell.com/modules/xfcontent/private.php/apparmor/Development%20-%20April%20Snapshot/${MY_P}.tar.gz" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~x86" +IUSE="" + +DEPEND="" +RDEPEND=">=sys-apps/apparmor-2.0" + +pkg_postinst() { + ewarn + ewarn "DO NOT EXPECT THESE PROFILES TO WORK ON YOUR SYSTEM!!!" + ewarn " Expect that ENABLING THEM WILL BREAK things" + ewarn " " + ewarn "These profiles were built to work with a SuSE base install, and " + ewarn "make many assumptions about file placement and system facilities " + ewarn "that are quite possibly untrue on any Gentoo system ever emerged." + ewarn " " + ewarn "They are provided for reference purposes only, until profiles can " + ewarn "be created and verified for Gentoo. If you are interested in using " + ewarn "AppArmor, and modifiying these base profiles for a Gentoo package, " + ewarn "please feel free to do so, and contact the AppArmor package " + ewarn "maintainer with your working profiles!" + ewarn + epause +} + +src_unpack() { + unpack ${A} + + cd ${MY_S} + # Move profiles to a different dir so that none of + # them are sourced by default on startup. + sed -i 's:apparmor.d$:apparmor.d/suse-defaults:g' Makefile +} + +src_compile() { + cd ${MY_S} + emake CC="$(tc-getCC)" CFLAGS="${CFLAGS}" || die +} + +src_install() { + cd ${MY_S} + # Place profiles in /usr/share, instead of /usr/src/Immunix + MY_SHAREDIR="/usr/share/${PN}" + make DESTDIR=${D} EXTRASDIR=${D}/${MY_SHAREDIR} install || die +} diff --git a/trunk/novell4gentoo/sec-policy/apparmor-profiles/files/digest-apparmor-profiles-2.0_p6376 b/trunk/novell4gentoo/sec-policy/apparmor-profiles/files/digest-apparmor-profiles-2.0_p6376 new file mode 100644 index 0000000..5144385 --- /dev/null +++ b/trunk/novell4gentoo/sec-policy/apparmor-profiles/files/digest-apparmor-profiles-2.0_p6376 @@ -0,0 +1,3 @@ +MD5 526e971c18cc6588271e9ad1787adf2a apparmor-profiles-2.0-6376.tar.gz 33651 +RMD160 cd64269b9c12fd60256f624b7b81aa1bddbb74c3 apparmor-profiles-2.0-6376.tar.gz 33651 +SHA256 55103b0ce98616b6860fb43eff23587ad0faf5e38ab828bdd025d87a55b0f073 apparmor-profiles-2.0-6376.tar.gz 33651 diff --git a/trunk/novell4gentoo/sec-policy/apparmor-profiles/metadata.xml b/trunk/novell4gentoo/sec-policy/apparmor-profiles/metadata.xml new file mode 100644 index 0000000..7e32869 --- /dev/null +++ b/trunk/novell4gentoo/sec-policy/apparmor-profiles/metadata.xml @@ -0,0 +1,5 @@ + + + +maintainer-wanted + diff --git a/trunk/novell4gentoo/sys-apps/apparmor/ChangeLog b/trunk/novell4gentoo/sys-apps/apparmor/ChangeLog new file mode 100644 index 0000000..0f96771 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/ChangeLog @@ -0,0 +1,11 @@ +# ChangeLog for sys-apps/apparmor +# Copyright 1999-2006 Gentoo Foundation; Distributed under the GPL v2 +# $Header: $ + + 13 Aug 2006; Mario Fetka +apparmor-2.0.ebuild, +TODO, + +files/aaeventd, +files/apparmor, +files/rc.apparmor.functions, + +files/rc.helper.functions: + Initial Import of + Matthew Snelham + AppArmor ebuilds + diff --git a/trunk/novell4gentoo/sys-apps/apparmor/Manifest b/trunk/novell4gentoo/sys-apps/apparmor/Manifest new file mode 100644 index 0000000..59cb15f --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/Manifest @@ -0,0 +1,30 @@ +AUX aaeventd 824 RMD160 032cce20f81b7b0e1f6f38cb6e1c392d70407ad5 SHA1 8adaa5e21867cab21c5fa907c974fb94cb280f70 SHA256 dbaa7fd1197388146c7a1a9b59553815fdcd0498510f7b7bf752af88f8eb1780 +MD5 379ee71ea946bd90c98e94e362bf9f2c files/aaeventd 824 +RMD160 032cce20f81b7b0e1f6f38cb6e1c392d70407ad5 files/aaeventd 824 +SHA256 dbaa7fd1197388146c7a1a9b59553815fdcd0498510f7b7bf752af88f8eb1780 files/aaeventd 824 +AUX apparmor 1078 RMD160 cd1d37be5839e2b97973b6ee24c0d1d039a8210e SHA1 1a6b1ca658d7af1da895a5be6347c4ff8f18fee6 SHA256 f74877c1d728db4529e1d1bfacb16f3fba28b5a0b0ba8d8ab49d9cd3089204bd +MD5 c2d82bbd26c81516622a8e1142969b9a files/apparmor 1078 +RMD160 cd1d37be5839e2b97973b6ee24c0d1d039a8210e files/apparmor 1078 +SHA256 f74877c1d728db4529e1d1bfacb16f3fba28b5a0b0ba8d8ab49d9cd3089204bd files/apparmor 1078 +AUX rc.apparmor.functions 11934 RMD160 42e76c294df2831deeb6037969e1373e8bc61a88 SHA1 eca09ad31c366dafe6291d2aceb4005bbbdc5374 SHA256 214eef64f76867295b446812df48faa90749d33a584e77070e2f84a576237519 +MD5 5fab485513f92704bf834161f129018a files/rc.apparmor.functions 11934 +RMD160 42e76c294df2831deeb6037969e1373e8bc61a88 files/rc.apparmor.functions 11934 +SHA256 214eef64f76867295b446812df48faa90749d33a584e77070e2f84a576237519 files/rc.apparmor.functions 11934 +AUX rc.helper.functions 546 RMD160 1a61b7d35491c8454fba1b5a892a37179e16b4fb SHA1 fda707b72b1498622b23aae0044abd9b7d0a9ab0 SHA256 6d58853c5945883a804e47b20e92369e106eb14390b6f8561cc5ccfe5418723e +MD5 91a6c3133fb4d0f7d35b5d25f587c01c files/rc.helper.functions 546 +RMD160 1a61b7d35491c8454fba1b5a892a37179e16b4fb files/rc.helper.functions 546 +SHA256 6d58853c5945883a804e47b20e92369e106eb14390b6f8561cc5ccfe5418723e files/rc.helper.functions 546 +DIST apparmor-docs-2.0-6269.tar.gz 2697657 RMD160 474048d988d72cbf9bf21d50e8aef37d06d9b000 SHA1 792f4339d86d02d179b786de87a47394abfdadae SHA256 9c2d63bfe42cc4582e24d1248a7560dd19cee718db513c7dedb12d45fe168221 +DIST apparmor-parser-2.0-25.tar.gz 154055 RMD160 371ed8a1c7b1ac2b7ee2e5add0225b709e7b9071 SHA1 aee31c18979d7ec8805c9f50300c7ed10b786e2d SHA256 fe83f57b8f588bad6de4527f375511b0d93b579dcd4f8bfb3d56c7e274782794 +DIST apparmor-utils-2.0-6379.tar.gz 97162 RMD160 b4b3647a62d495e58cf476d6af1eaa7afc41daa5 SHA1 e1336bcecfffda464c8a1b8bd4883f4e52928cf5 SHA256 f4c819a36457f4ce53d50d3266f16b5801e5a5e583f18ad9df239e230d3a5465 +EBUILD apparmor-2.0.ebuild 2557 RMD160 7df4146acdb534002908f38fdf18ce949f734be4 SHA1 4874525556509133fea655c5e18f1c35f3a582c0 SHA256 4bd511580c5467a237e2f5144f85aa1dd1f60c729645ed714b88ea6a1059577e +MD5 4e8616789c3911c43a4fc67fd281e902 apparmor-2.0.ebuild 2557 +RMD160 7df4146acdb534002908f38fdf18ce949f734be4 apparmor-2.0.ebuild 2557 +SHA256 4bd511580c5467a237e2f5144f85aa1dd1f60c729645ed714b88ea6a1059577e apparmor-2.0.ebuild 2557 +MISC TODO 299 RMD160 07fdbfb908c9781718553cdf96b1f0a131540fef SHA1 33d47764bcf82c09f5aed67e3c38bbd5de0e6c18 SHA256 df384239f315f82c9c21b30bbd0e602007e7d3a251cba865738e8ef77951495f +MD5 a291d308d2e8c88e92cf1dc0b8b3ee2a TODO 299 +RMD160 07fdbfb908c9781718553cdf96b1f0a131540fef TODO 299 +SHA256 df384239f315f82c9c21b30bbd0e602007e7d3a251cba865738e8ef77951495f TODO 299 +MD5 55d92fc75639f1bf39ae317b69b74048 files/digest-apparmor-2.0 807 +RMD160 8e27a3a7d5ad23b2b516818272f9d89e8c120ad5 files/digest-apparmor-2.0 807 +SHA256 0345343eced1c6ac9e5604e7c911eb72aeb02c2487084e267de66c7ac5ffc6ba files/digest-apparmor-2.0 807 diff --git a/trunk/novell4gentoo/sys-apps/apparmor/TODO b/trunk/novell4gentoo/sys-apps/apparmor/TODO new file mode 100644 index 0000000..e82a6ca --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/TODO @@ -0,0 +1,8 @@ + +Clean-up legacy code, where reasonable: + clean up init.d functions to enable Gentoo init conformance + ## update rc.helper function defs for Gentoo + update rc.helper functions for other distros for push upstream + remove subdomain_parser man pages from doc section + +Enable OWLSM submodule function diff --git a/trunk/novell4gentoo/sys-apps/apparmor/apparmor-2.0.ebuild b/trunk/novell4gentoo/sys-apps/apparmor/apparmor-2.0.ebuild new file mode 100644 index 0000000..29da087 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/apparmor-2.0.ebuild @@ -0,0 +1,118 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +inherit eutils perl-module + +PARSER_PN="apparmor-parser" +PARSER_PV="${PV}-25" +PARSER_P="${PARSER_PN}-${PARSER_PV}" +PARSER_S="${WORKDIR}/${PARSER_PN}-${PV}" + +UTIL_PN="apparmor-utils" +UTIL_PV="${PV}-6379" +UTIL_P="${UTIL_PN}-${UTIL_PV}" +UTIL_S="${WORKDIR}/${UTIL_PN}-${PV}" + +DOC_PN="apparmor-docs" +DOC_PV="${PV}-6269" +DOC_P="${DOC_PN}-${DOC_PV}" +DOC_S="${WORKDIR}/${DOC_PN}-${PV}" + +DESCRIPTION="AppArmor provides easy to use application level security controls via the kernel Linux Security Modules interface." +HOMEPAGE="http://forge.novell.com/modules/xfmod/project/?apparmor" +SRC_URI="http://forge.novell.com/modules/xfcontent/private.php/${PN}/Development%20-%20April%20Snapshot/${PARSER_P}.tar.gz \ +http://forge.novell.com/modules/xfcontent/private.php/${PN}/Development%20-%20April%20Snapshot/${UTIL_P}.tar.gz \ +http://forge.novell.com/modules/xfcontent/private.php/apparmor/Development%20-%20April%20Snapshot/${DOC_P}.tar.gz" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~x86" +IUSE="" + +DEPEND="=sys-libs/libapparmor-2.0_p6288 + sys-libs/libcap + dev-lang/perl + perl-core/Test-Harness + perl-core/Getopt-Long + dev-perl/DBI + dev-perl/DBD-SQLite + dev-perl/TimeDate + dev-perl/File-Tail + dev-perl/Locale-gettext + dev-libs/libpcre + sys-devel/bison + sys-devel/flex" + +RDEPEND="sec-policy/apparmor-profiles" +# =sys-kernel/apparmor-sources-2.6.17 +# ~sys-apps/apparmor-mod-2.0" + +src_unpack() { + unpack ${A} + + ## apparmor-parser + cd ${PARSER_S} + # the Make.rules isn't needed for Gentoo + sed -i "s/^include Make.rules//g" Makefile + + ## apparmor-docs + cd ${DOC_S} + sed -i "s:NOVELL/SUSE:Gentoo:g" Makefile +} + +src_compile() { + S_PKGS="${PARSE_S} ${UTIL_S} ${DOC_S}" + + for pkg in ${S_PKGS}; do + cd ${pkg} + emake CC="$(tc-getCC)" CFLAGS="${CFLAGS}" || die + done +} + +src_install() { + + ## apparmor-parser + cd ${PARSER_S} + make DESTDIR=${D} install || die + + ## apparmor-utils + cd ${UTIL_S} + perlinfo + make DESTDIR=${D} PERLDIR="${D}/${VENDOR_LIB}/Immunix" install || die + + ## apparmor-docs + cd ${DOC_S} + make DESTDIR=${D} install_manpages || die + # Some files are missing from the doc distribution, so no '|| die' + # bug submitted upstream + make DESTDIR=${D} DOCDIR=/usr/share/doc/${P} install_documents + + ## Init script and addtional files + doinitd ${FILESDIR}/apparmor + doinitd ${FILESDIR}/aaeventd + insopts -m0644 + insinto /lib/apparmor + doins ${FILESDIR}/rc.helper.functions + doins ${FILESDIR}/rc.apparmor.functions + + dodir /etc/apparmor.d/abstractions +} + +pkg_postinst() { + ewarn + ewarn "TO USE APPARMOR YOU NEED AN APPARMOR AWARE KERNEL!!!" + ewarn " sys-kernel/apparmor-sources are apparmor aware" +# ewarn " " +# ewarn "These profiles were built to work with a SuSE base install, and " +# ewarn "make many assumptions about file placement and system facilities " +# ewarn "that are quite possibly untrue on any Gentoo system ever emerged." +# ewarn " " +# ewarn "They are provided for reference purposes only, until profiles can " +# ewarn "be created and verified for Gentoo. If you are interested in using " +# ewarn "AppArmor, and modifiying these base profiles for a Gentoo package, " +# ewarn "please feel free to do so, and contact the AppArmor package " +# ewarn "maintainer with your working profiles!" +# ewarn + epause +} diff --git a/trunk/novell4gentoo/sys-apps/apparmor/files/aaeventd b/trunk/novell4gentoo/sys-apps/apparmor/files/aaeventd new file mode 100755 index 0000000..efe9cc3 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/files/aaeventd @@ -0,0 +1,40 @@ +#!/sbin/runscript +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions +APPARMOR_HELPERS=/lib/apparmor/rc.helper.functions +if [ -f "${APPARMOR_HELPERS}" -a -f "${APPARMOR_FUNCTIONS}" ]; then + source ${APPARMOR_HELPERS} + source ${APPARMOR_FUNCTIONS} +else + eend 1 "Unable to find AppArmor initscript functions" +fi + +depend() { + need apparmor + use logger dns +} + +start() { + ebegin "Starting aaeventd (AppArmor Event Daemon)" + + if [ "${APPARMOR_ENABLE_AAEVENTD}" = "no" ]; then + eend 1 " aaeventd disabled in ${APPARMOR_CONF}." + fi + + start_sd_event + eend $waserror +} + +stop() { + ebegin "Stopping aaeventd (AppArmor Event Daemon)" + stop_sd_event + eend $waserror +} + +restart() { + srv_stop + srv_start +} diff --git a/trunk/novell4gentoo/sys-apps/apparmor/files/apparmor b/trunk/novell4gentoo/sys-apps/apparmor/files/apparmor new file mode 100755 index 0000000..643fe50 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/files/apparmor @@ -0,0 +1,56 @@ +#!/sbin/runscript +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ +# +# rc.apparmor.gentoo by Matthew Snelham +# +# /etc/init.d/apparmor + +APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions +APPARMOR_HELPERS=/lib/apparmor/rc.helper.functions +if [ -f "${APPARMOR_HELPERS}" -a -f "${APPARMOR_FUNCTIONS}" ]; then + source ${APPARMOR_HELPERS} + source ${APPARMOR_FUNCTIONS} +else + eend 1 "Unable to find AppArmor initscript functions" +fi + +opts="${opts} reload status" + +depend() { + need localmount + ## AppArmor needs to be loaded before any other + ## (potentially protected) user-space services come up. + #before * +} + +start() { + ebegin "Starting AppArmor" + subdomain_start + einfo "...AppArmor Start" + eend $waserror +} + +stop() { + ebegin "Stopping AppArmor" + subdomain_stop + einfo "...AppArmor Stop" + eend $waserror +} + +restart() { + svc_stop; svc_start +} + +reload() { + ebegin "Restarting AppArmor" + subdomain_restart + eend $? +} + +status() { + ebegin "Checking AppArmor Status" + subdomain_status + eend $? +} diff --git a/trunk/novell4gentoo/sys-apps/apparmor/files/digest-apparmor-2.0 b/trunk/novell4gentoo/sys-apps/apparmor/files/digest-apparmor-2.0 new file mode 100644 index 0000000..e80cd26 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/files/digest-apparmor-2.0 @@ -0,0 +1,9 @@ +MD5 a5130a30a6a9e7cac316fe7658af031a apparmor-docs-2.0-6269.tar.gz 2697657 +RMD160 474048d988d72cbf9bf21d50e8aef37d06d9b000 apparmor-docs-2.0-6269.tar.gz 2697657 +SHA256 9c2d63bfe42cc4582e24d1248a7560dd19cee718db513c7dedb12d45fe168221 apparmor-docs-2.0-6269.tar.gz 2697657 +MD5 1486ed6062435ff82340d6d9967b4df6 apparmor-parser-2.0-25.tar.gz 154055 +RMD160 371ed8a1c7b1ac2b7ee2e5add0225b709e7b9071 apparmor-parser-2.0-25.tar.gz 154055 +SHA256 fe83f57b8f588bad6de4527f375511b0d93b579dcd4f8bfb3d56c7e274782794 apparmor-parser-2.0-25.tar.gz 154055 +MD5 0144c81537fd724eaa6e23822edaaae1 apparmor-utils-2.0-6379.tar.gz 97162 +RMD160 b4b3647a62d495e58cf476d6af1eaa7afc41daa5 apparmor-utils-2.0-6379.tar.gz 97162 +SHA256 f4c819a36457f4ce53d50d3266f16b5801e5a5e583f18ad9df239e230d3a5465 apparmor-utils-2.0-6379.tar.gz 97162 diff --git a/trunk/novell4gentoo/sys-apps/apparmor/files/rc.apparmor.functions b/trunk/novell4gentoo/sys-apps/apparmor/files/rc.apparmor.functions new file mode 100755 index 0000000..56c3114 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/files/rc.apparmor.functions @@ -0,0 +1,443 @@ +#!/bin/sh +# +# ---------------------------------------------------------------------- +# Copyright (c) 1999, 2000, 20001, 2004, 2005, NOVELL (All rights reserved) +# +# 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. +# ---------------------------------------------------------------------- +# rc.subdomain.functions by Steve Beattie +# Modified for Gentoo Linux, by Matthew Snelham +# +# Modifications Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + + +# NOTE: rc.subdomain initscripts that source this file need to implement +# the following set of functions: +# sd_action +# sd_log_info_msg +# sd_log_success_msg +# sd_log_warning_msg +# sd_log_failure_msg + + +CONFIG_DIR=/etc/apparmor +MODULE=apparmor +OLD_MODULE=subdomain +if [ -f "${CONFIG_DIR}/${MODULE}.conf" ] ; then + APPARMOR_CONF="${CONFIG_DIR}/${MODULE}.conf" +elif [ -f "${CONFIG_DIR}/${OLD_MODULE}.conf" ] ; then + APPARMOR_CONF="${CONFIG_DIR}/${OLD_MODULE}.conf" +else + sd_log_warning_msg "Unable to find config file in ${CONFIG_DIR}, installation problem?" +fi + +# Read configuration options from ${APPARMOR_CONF}, default is to +# warn if subdomain won't load. +APPARMOR_MODULE_PANIC="warn" +SUBDOMAIN_ENABLE_OWLSM="no" +APPARMOR_ENABLE_AAEVENTD="no" + +if [ -f "${APPARMOR_CONF}" ] ; then + source "${APPARMOR_CONF}" +fi + +if [ -f /sbin/apparmor_parser ] ; then + PARSER=/sbin/apparmor_parser +else + sd_log_failure_msg "Unable to find apparmor_parser, installation problem?" + exit 1 +fi + +# APPARMOR_DIR might be redefined in ${APPARMOR_CONF} +if [ -d "${APPAMROR_DIR}" ] ; then + PROFILE_DIR=${APPARMOR_DIR} +elif [ -d /etc/apparmor.d ] ; then + PROFILE_DIR=/etc/apparmor.d +fi +ABSTRACTIONS="-I${PROFILE_DIR}" +AA_EV_BIN=/usr/sbin/aa-eventd +AA_EV_PIDFILE=/var/run/aa-eventd.pid +AA_STATUS=/usr/sbin/apparmor_status +SD_EV_BIN=/usr/sbin/sd-event-dispatch.pl +SD_EV_PIDFILE=/var/run/sd-event-dispatch.init.pid +SD_STATUS=/usr/sbin/subdomain_status +if grep -q securityfs /proc/filesystems ; then + SECURITYFS=/sys/kernel/security +fi + +SUBDOMAINFS_MOUNTPOINT=$(grep subdomainfs /etc/fstab | \ + sed -e 's|^[[:space:]]*[^[:space:]]\+[[:space:]]\+\(/[^[:space:]]*\)[[:space:]]\+subdomainfs.*$|\1|' 2> /dev/null) + +if [ -d "/var/lib/${MODULE}" ] ; then + APPARMOR_TMPDIR="/var/lib/${MODULE}" +else + APPARMOR_TMPDIR="/tmp" +fi + + +function parse_profiles() { + # get parser arg + case "$1" in + load) + PARSER_ARGS="--add" + PARSER_MSG="Loading AppArmor profiles " + ;; + reload) + PARSER_ARGS="--replace" + PARSER_MSG="Reloading AppArmor profiles " + ;; + *) + exit 1 + ;; + esac + sd_log_info_msg "$PARSER_MSG" + + # run the parser on all of the apparmor profiles + if [ ! -f "$PARSER" ]; then + sd_log_failure_msg "$PARSER_MSG - AppArmor parser not found" + exit 1 + fi + + if [ ! -d "$PROFILE_DIR" ]; then + sd_log_failure_msg "$PARSER_MSG - Profile directory not found" + exit 1 + fi + + if [ "X" == "X$(ls $PROFILE_DIR/)" ]; then + sd_log_warning_msg "$PARSER_MSG - No profiles found" + exit 1 + fi + + for profile in $PROFILE_DIR/*; do + if [ "${profile%.rpmnew}" != "${profile}" -o \ + "${profile%.rpmsave}" != "${profile}" -o \ + "${profile%\~}" != "${profile}" ] + then + sd_log_warning_msg "Skipping profile $profile" + elif [ -f "${profile}" ] ; then + sd_action " Adding profile: `basename ${profile}`" $PARSER $ABSTRACTIONS $PARSER_ARGS ${profile} + if [ $? -ne 0 ]; then + waserror=1 + fi + fi + done +} + +function profiles_names_list() { + # run the parser on all of the apparmor profiles + TMPFILE=$1 + if [ ! -f "$PARSER" ]; then + sd_log_failure_msg "AppArmor parser ($PARSER) not found" + exit 1 + fi + + if [ ! -d "$PROFILE_DIR" ]; then + sd_log_failure_msg "Profile directory ($PROFILE_DIR) not found" + exit 1 + fi + + for profile in $PROFILE_DIR/*; do + if [ "${profile%.rpmnew}" != "${profile}" -o \ + "${profile%.rpmsave}" != "${profile}" -o \ + "${profile%\~}" != "${profile}" ] + then + echo "nop" >/dev/null + elif [ -f "${profile}" ] ; then + LIST_ADD=$($PARSER $ABSTRACTIONS -N "$profile" | grep -v '\^') + if [ $? -eq 0 ]; then + echo "$LIST_ADD" >>$TMPFILE + fi + fi + done +} + +function is_securityfs_mounted() { + if grep -q securityfs /proc/filesystems && grep -q securityfs /proc/mounts ; then + if [ -f "${SECURITYFS}/${MODULE}/profiles" ]; then + SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}" + return 0 + fi + fi + return 1 +} + +function mount_securityfs() { + if [ "X" != "X${SECURITYFS}" ]; then + if ! grep -q securityfs /proc/mounts ; then + sd_action "Mounting securityfs on ${SECURITYFS}" \ + mount -t securityfs securityfs "${SECURITYFS}" + rc=$? + if [ -f "${SECURITYFS}/${MODULE}/profiles" ]; then + SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}" + else + SFS_MOUNTPOINT="${SECURITYFS}/${MODULE}" + fi + return $rc + fi + fi + return 0 +} + +function unmount_securityfs() { + SUBDOMAINFS=$(grep subdomainfs /proc/mounts | cut -d" " -f2 2> /dev/null) + if [ "X" != "X${SUBDOMAINFS}" ]; then + sd_action "Unmounting securityfs" umount ${SUBDOMAINFS} + fi +} + +function failstop_system() { + level=$(runlevel | cut -d" " -f2) + if [ $level -ne "1" ] ; then + sd_log_failure_msg "Could not start AppArmor. Changing to runlevel 1" + telinit 1; + return -1; + fi + sd_log_failure_msg "Could not start AppArmor." + return -1 +} + +function module_panic() { + # the module failed to load, determine what action should be taken + + case "$APPARMOR_MODULE_PANIC" in + "warn"|"WARN") sd_log_failure_msg "Could not start AppArmor" + return -1 ;; + "panic"|"PANIC") failstop_system + rc=$? + return $rc ;; + *) sd_log_failure_msg "Invalid AppArmor module fail option" + return -1 ;; + esac +} + +function load_module() { + if modinfo -F filename apparmor > /dev/null 2>&1 ; then + MODULE=apparmor + elif modinfo -F filename subdomain > /dev/null 2>&1 ; then + MODULE=subdomain + fi + if ! grep -qE "^(subdomain|apparmor)[[:space:]]" /proc/modules ; then + sd_action "Loading AppArmor module" /sbin/modprobe $MODULE $1 + rc=$? + if [ $rc -ne 0 ] ; then + # we couldn't find the module + module_panic + rc=$? + if [ $rc -ne 0 ] ; then + exit $rc + fi + fi + fi +} + +function start_sd_event() { + if [ -x "$AA_EV_BIN" -a "${APPARMOR_ENABLE_AAEVENTD}" = "yes" ] ; then + sd_action "Starting AppArmor Event daemon" startproc -f -p $AA_EV_PIDFILE $AA_EV_BIN -p $AA_EV_PIDFILE + elif [ -x "$SD_EV_BIN" -a "${APPARMOR_ENABLE_AAEVENTD}" = "yes" ] ; then + sd_action "Starting AppArmor Event daemon" startproc -f -p $SD_EV_PIDFILE $SD_EV_BIN -p $SD_EV_PIDFILE + fi +} + +function stop_sd_event() { + if [ -x "$AA_EV_BIN" -a -f "$AA_EV_PIDFILE" ] ; then + sd_action "Shutting down AppArmor Event daemon" /sbin/killproc -G -p $AA_EV_PIDFILE -INT $AA_EV_BIN + fi + if [ -f "$SD_EV_PIDFILE" ] ; then + sd_action "Shutting down AppArmor Event daemon" /sbin/killproc -G -p $SD_EV_PIDFILE -INT $SD_EV_BIN + fi +} + +function subdomain_start() { + if ! grep -qE "^(subdomain|apparmor)[[:space:]]" /proc/modules ; then + load_module + rc=$? + if [ $rc -ne 0 ] ; then + return $rc + fi + fi + + if ! is_securityfs_mounted ; then + mount_securityfs + rc=$? + if [ $rc -ne 0 ] ; then + return $rc + fi + fi + + if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then + sd_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?" + return 1 + fi + + configure_owlsm + + if [ $(wc -l "$SFS_MOUNTPOINT/profiles" | awk '{print $1}') -eq 0 ] ; then + parse_profiles load + else + sd_log_warning_msg "Loading AppArmor profiles - AppArmor already loaded with profiles. Not loading profiles." + fi +} + +function remove_profiles() { + # removing profiles as we directly read from subdomainfs + # doesn't work, since we are removing entries which screws up + # our position. Lets hope there are never enough profiles to + # overflow the variable + if ! is_securityfs_mounted ; then + sd_log_failure_msg "failed: is securityfs loaded?" + return 1 + fi + + if [ ! -w "$SFS_MOUNTPOINT/.remove" ] ; then + sd_log_failure_msg "failed: Do you have the correct privileges?" + return 1 + fi + + if [ ! -x "${PARSER}" ] ; then + sd_log_failure_msg "failed: unable to execute subdomain parser" + return 1 + fi + + retval=0 + IFS=$'\n' + enforced_profiles=$(sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles") + for profile in $enforced_profiles ; do + sd_action " Removing profile: ${profile}" sh -c "echo \"$profile { }\" | $PARSER -R" + rc=$? + if [ ${rc} -ne 0 ] ; then + retval=${rc} + fi + done + if [ ${retval} -ne 0 ] ; then + waserror=1 + fi +} + +function subdomain_stop() { + stop_sd_event + sd_log_info_msg "Unloading AppArmor profiles" + remove_profiles +} + +function subdomain_kill() { + stop_sd_event + unmount_securityfs + if grep -qE "^apparmor[[:space:]]" /proc/modules ; then + MODULE=apparmor + elif grep -qE "^subdomain[[:space:]]" /proc/modules ; then + MODULE=subdomain + else + MODULE=apparmor + fi + sd_action "Unloading AppArmor modules" /sbin/modprobe -r $MODULE +} + +function __subdomain_restart() { + if [ ! -w "$SFS_MOUNTPOINT/.load" ] ; then + sd_log_failure_msg "Loading AppArmor profiles - failed, Do you have the correct privileges?" + return 4 + fi + + configure_owlsm + parse_profiles reload + PNAMES_LIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX) + profiles_names_list ${PNAMES_LIST} + MODULE_PLIST=$(mktemp ${APPARMOR_TMPDIR}/tmp.XXXXXXXX) + sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | sort >"$MODULE_PLIST" + #profiles=$(cat $PNAMES_LIST | sort | comm -2 -3 "$MODULE_PLIST" -) + #for profile in $profiles ; do + IFS=$'\n' && for profile in $(cat $PNAMES_LIST | sort | comm -2 -3 "$MODULE_PLIST" -) ; do + echo "\"$profile\" {}" | $PARSER -R >/dev/null + done + rm "$MODULE_PLIST" + rm "$PNAMES_LIST" + return 0 +} + +function subdomain_restart() { + if ! grep -qE "^(subdomain|apparmor)[[:space:]]" /proc/modules ; then + subdomain_start + rc=$? + return $rc + fi + + if ! is_securityfs_mounted ; then + mount_securityfs + rc=$? + if [ $rc -ne 0 ] ; then + return $rc + fi + fi + + __subdomain_restart + rc=$? + return $rc +} + +function subdomain_try_restart() { + if ! grep -qE "^(subdomain|apparmor)[[:space:]]" /proc/modules ; then + return 1 + fi + + if ! is_securityfs_mounted ; then + return 1 + fi + + __subdomain_restart + rc=$? + return $rc +} + +function subdomain_debug() { + subdomain_kill + load_module "subdomain_debug=1" + mount_securityfs + configure_owlsm + parse_profiles load +} + +function configure_owlsm () { + if [ "${SUBDOMAIN_ENABLE_OWLSM}" = "yes" -a -f ${SFS_MOUNTPOINT}/control/owlsm ] ; then + # Sigh, the "sh -c" is necessary for the SuSE sd_action + # and it can't be abstracted out as a seperate function, as + # that breaks under RedHat's action, which needs a + # binary to invoke. + sd_action "Enabling OWLSM extension" sh -c "echo -n \"1\" > \"${SFS_MOUNTPOINT}/control/owlsm\"" + elif [ -f "${SFS_MOUNTPOINT}/control/owlsm" ] ; then + sd_action "Disabling OWLSM extension" sh -c "echo -n \"0\" > \"${SFS_MOUNTPOINT}/control/owlsm\"" + fi +} + +function subdomain_status () { + if test -x ${AA_STATUS} ; then + ${AA_STATUS} --verbose + return $? + fi + if test -x ${SD_STATUS} ; then + ${SD_STATUS} --verbose + return $? + fi + if ! grep -qE "^(subdomain|apparmor)[[:space:]]" /proc/modules ; then + sd_log_failure_msg "AppArmor not loaded." + rc=1 + else + sd_log_success_msg "AppArmor module enabled." + rc=0 + fi + sd_log_warning_msg "Install the apparmor-utils package to receive more detailed" + sd_log_warning_msg "status information here (or examine ${SFS_MOUNTPOINT} directly)." + + return $rc +} diff --git a/trunk/novell4gentoo/sys-apps/apparmor/files/rc.helper.functions b/trunk/novell4gentoo/sys-apps/apparmor/files/rc.helper.functions new file mode 100644 index 0000000..f76fb7d --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/files/rc.helper.functions @@ -0,0 +1,38 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +waserror=0 + +function sd_action() { + MSG=$1 + shift + #echo "ACTION: $*" + $* > /dev/null + rc=$? + if [ $rc -ne 0 ] ; then + sd_log_failure_msg $"$MSG" + else + sd_log_success_msg $"$MSG" + fi + return $rc +} + +function sd_log_info_msg() { + einfo " $1" +} + +function sd_log_warning_msg() { + ewarn " $1" +} + +function sd_log_success_msg() { + einfo " $1" + eend 0 +} + +function sd_log_failure_msg() { + waserror=1 + einfo " $1" + eend 1 +} diff --git a/trunk/novell4gentoo/sys-apps/apparmor/metadata.xml b/trunk/novell4gentoo/sys-apps/apparmor/metadata.xml new file mode 100644 index 0000000..7e32869 --- /dev/null +++ b/trunk/novell4gentoo/sys-apps/apparmor/metadata.xml @@ -0,0 +1,5 @@ + + + +maintainer-wanted + diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/ChangeLog b/trunk/novell4gentoo/sys-kernel/apparmor-sources/ChangeLog new file mode 100644 index 0000000..64e901f --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/ChangeLog @@ -0,0 +1,23 @@ +# ChangeLog for sys-kernel/apparmor-sources +# Copyright 1999-2006 Gentoo Foundation; Distributed under the GPL v2 +# $Header: $ + + 13 Aug 2006; Mario Fetka + +files/5000_apparmor-integrate.patch, + +files/5001_apparmor-core-header.patch, + +files/5002_apparmor-lsm-interface.patch, + +files/5004_apparmor-filesystem.patch, +apparmor-sources-2.6.17.ebuild, + +files/5003_apparmor-core-access-controls.patch, + +files/5006_apparmor-misc.patch, +files/5008_apparmor-audit-changes.patch, + +apparmor-sources-2.6.17-r1.ebuild, + +files/apparmor-sources-2.6.17-r1-apparmor_main.patch, + +files/5007_apparmor-pathname-matching-submodule.patch, + +files/5010_apparmor-export-namespace-semaphor.patch, + +files/apparmor-sources-2.6.17-r1-apparmor_audit.patch, + +files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch, + +files/5005_apparmor-userspace-interface.patch, + +files/5009_apparmor-add-flags.patch: + Initial Import of + Matthew Snelham + Apparmor ebuilds + diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/Manifest b/trunk/novell4gentoo/sys-kernel/apparmor-sources/Manifest new file mode 100644 index 0000000..f2ff591 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/Manifest @@ -0,0 +1,73 @@ +AUX 5000_apparmor-integrate.patch 2398 RMD160 4f3100e01a64531217137303b151d4b8f57cb5ea SHA1 c61a433e8449cd2e173eaf4526de807cd074d12b SHA256 558a025803b209d2502562553e7ec1a42dde706c6a3de4d1a442f4a4a17e8b89 +MD5 d98fc30c0c02d3b6e1fdb5a56973ed67 files/5000_apparmor-integrate.patch 2398 +RMD160 4f3100e01a64531217137303b151d4b8f57cb5ea files/5000_apparmor-integrate.patch 2398 +SHA256 558a025803b209d2502562553e7ec1a42dde706c6a3de4d1a442f4a4a17e8b89 files/5000_apparmor-integrate.patch 2398 +AUX 5001_apparmor-core-header.patch 21692 RMD160 d991a62378cf9394ed785ce170567c5a1636ed3a SHA1 6646b0ab9603356241ca09ac26093649b264e6f5 SHA256 104c8a90f300b967937d2930bfdb46fccc14d1c33e0eeeaccc2539795b02c9f7 +MD5 589ca3ad54269790ce4ff0ac86bf1e22 files/5001_apparmor-core-header.patch 21692 +RMD160 d991a62378cf9394ed785ce170567c5a1636ed3a files/5001_apparmor-core-header.patch 21692 +SHA256 104c8a90f300b967937d2930bfdb46fccc14d1c33e0eeeaccc2539795b02c9f7 files/5001_apparmor-core-header.patch 21692 +AUX 5002_apparmor-lsm-interface.patch 21459 RMD160 b61ddaab908a7a790472d7de3c0ee03dfe576489 SHA1 464d3806b9db782adfba98e37884f538f4f02fdd SHA256 8f22edd4509598b9188ef05b47028a745905af4402482f1698bf123f66d3b407 +MD5 2ef63832bbbef8442cbf8a08d0279425 files/5002_apparmor-lsm-interface.patch 21459 +RMD160 b61ddaab908a7a790472d7de3c0ee03dfe576489 files/5002_apparmor-lsm-interface.patch 21459 +SHA256 8f22edd4509598b9188ef05b47028a745905af4402482f1698bf123f66d3b407 files/5002_apparmor-lsm-interface.patch 21459 +AUX 5003_apparmor-core-access-controls.patch 42703 RMD160 fa685b1526b69cc5e436707b0e94513c963cf7e5 SHA1 756e4adac0c15e216b21c4b86bd390d2318f2092 SHA256 585f135ce0b07bc325503cccd5da07afa559ca75c9c3c0aeef3b1309f59aacb5 +MD5 282fc71ae3ba0b0f37b10e856cb0bbb7 files/5003_apparmor-core-access-controls.patch 42703 +RMD160 fa685b1526b69cc5e436707b0e94513c963cf7e5 files/5003_apparmor-core-access-controls.patch 42703 +SHA256 585f135ce0b07bc325503cccd5da07afa559ca75c9c3c0aeef3b1309f59aacb5 files/5003_apparmor-core-access-controls.patch 42703 +AUX 5004_apparmor-filesystem.patch 11823 RMD160 c356279b0a7aa3c86b7e61fa4fd19fcae01e4e28 SHA1 7bac3503d5d3c1c89c3c83824ce4db5fb4b76d4d SHA256 8ff511068c44f09fa9e9b0892323876373a0dd20cbdafab1847439fb7293228a +MD5 9b00001fd17410a6c5c8d93e1d791a4f files/5004_apparmor-filesystem.patch 11823 +RMD160 c356279b0a7aa3c86b7e61fa4fd19fcae01e4e28 files/5004_apparmor-filesystem.patch 11823 +SHA256 8ff511068c44f09fa9e9b0892323876373a0dd20cbdafab1847439fb7293228a files/5004_apparmor-filesystem.patch 11823 +AUX 5005_apparmor-userspace-interface.patch 25246 RMD160 1aa62cbead83275308c9b2e5bf3658261d1a9381 SHA1 93a99de540ffbec0946192851832b823956cb3a6 SHA256 970934bf9e9d9a4e98e4d78eb4540d1b8d5409f5c97bbbd266ae473b7263d92f +MD5 f9ca3674c76ad58917df78d9ca55a461 files/5005_apparmor-userspace-interface.patch 25246 +RMD160 1aa62cbead83275308c9b2e5bf3658261d1a9381 files/5005_apparmor-userspace-interface.patch 25246 +SHA256 970934bf9e9d9a4e98e4d78eb4540d1b8d5409f5c97bbbd266ae473b7263d92f files/5005_apparmor-userspace-interface.patch 25246 +AUX 5006_apparmor-misc.patch 16419 RMD160 7be689597785f339dd8d8d41fc46c48283acd329 SHA1 728e32285aeb1e8e7ef28c2d8aa8a775ad4b1ab2 SHA256 c44468fd5a698d11095cccb439541feac587388f348bec5f8105449294e1b8f6 +MD5 eda12351340ca5e03ce2df5be7ab1396 files/5006_apparmor-misc.patch 16419 +RMD160 7be689597785f339dd8d8d41fc46c48283acd329 files/5006_apparmor-misc.patch 16419 +SHA256 c44468fd5a698d11095cccb439541feac587388f348bec5f8105449294e1b8f6 files/5006_apparmor-misc.patch 16419 +AUX 5007_apparmor-pathname-matching-submodule.patch 7130 RMD160 53203c04619a1d1491f32af7b67d645cab2b42fd SHA1 f6e08796e9de778de5614f7dc0b3226ec3e0c886 SHA256 08f8cef053a986ac38130743cba28a584c0539f1e7d370cdff3e829e4cd48567 +MD5 2d77b49c08b0f975660174c410f57cfe files/5007_apparmor-pathname-matching-submodule.patch 7130 +RMD160 53203c04619a1d1491f32af7b67d645cab2b42fd files/5007_apparmor-pathname-matching-submodule.patch 7130 +SHA256 08f8cef053a986ac38130743cba28a584c0539f1e7d370cdff3e829e4cd48567 files/5007_apparmor-pathname-matching-submodule.patch 7130 +AUX 5008_apparmor-audit-changes.patch 2107 RMD160 d333371405ac7c595763bc35f312784cc811d366 SHA1 8801e85d409017c6693656b9e41973703170cad8 SHA256 adfed39273b71c470f8eebddf43f0eef2146b3114e517eb574f033005f5a95e5 +MD5 2a4a92e7b58dafaee09f69fa05ebd89a files/5008_apparmor-audit-changes.patch 2107 +RMD160 d333371405ac7c595763bc35f312784cc811d366 files/5008_apparmor-audit-changes.patch 2107 +SHA256 adfed39273b71c470f8eebddf43f0eef2146b3114e517eb574f033005f5a95e5 files/5008_apparmor-audit-changes.patch 2107 +AUX 5009_apparmor-add-flags.patch 5477 RMD160 fc48784cb74cba9cdbdf61c2f561e8951152344e SHA1 f70cad7a35362ff584991c70c1b481127e4b3799 SHA256 adae3a61e66d527586d8118b94843b3021b4c99224d2d8888e37f7dbed5ef783 +MD5 29efc91d396797479864537b9f1e1b6f files/5009_apparmor-add-flags.patch 5477 +RMD160 fc48784cb74cba9cdbdf61c2f561e8951152344e files/5009_apparmor-add-flags.patch 5477 +SHA256 adae3a61e66d527586d8118b94843b3021b4c99224d2d8888e37f7dbed5ef783 files/5009_apparmor-add-flags.patch 5477 +AUX 5010_apparmor-export-namespace-semaphor.patch 2052 RMD160 70bae68ca3aef38c9aea0b4add916fc759efb420 SHA1 4f553490593ebaddec1a8695e6da471466e4cc70 SHA256 42fd27dc8e65a450b6369638495ebcd1d615a23881f4ff7c4a8a334ac6e1e00a +MD5 5d38d075644b24d5018152b2b4a29514 files/5010_apparmor-export-namespace-semaphor.patch 2052 +RMD160 70bae68ca3aef38c9aea0b4add916fc759efb420 files/5010_apparmor-export-namespace-semaphor.patch 2052 +SHA256 42fd27dc8e65a450b6369638495ebcd1d615a23881f4ff7c4a8a334ac6e1e00a files/5010_apparmor-export-namespace-semaphor.patch 2052 +AUX apparmor-sources-2.6.17-r1-apparmor_audit.patch 2247 RMD160 d6c5937273d30de4a83a42ad9256416bf2931a03 SHA1 113abbb7664e6cf205606c78c39086c4aea5883d SHA256 27b8c610ec1dd4b6b8474194061a6616359aa942f3dddf3ae197cadeea8e411c +MD5 aa55bffe2d0bf05ef6b6281dfb4c459c files/apparmor-sources-2.6.17-r1-apparmor_audit.patch 2247 +RMD160 d6c5937273d30de4a83a42ad9256416bf2931a03 files/apparmor-sources-2.6.17-r1-apparmor_audit.patch 2247 +SHA256 27b8c610ec1dd4b6b8474194061a6616359aa942f3dddf3ae197cadeea8e411c files/apparmor-sources-2.6.17-r1-apparmor_audit.patch 2247 +AUX apparmor-sources-2.6.17-r1-apparmor_main.patch 219968 RMD160 8ac7a4ee65da4180684634b82bd468e2ff0ff790 SHA1 610451e2eb4c7b03ed4a37d527543dea8a0f6743 SHA256 f799f9f570dd1de355aad61a3583a6e06aba6138f4b7c064274ad524d4c6ab18 +MD5 665706462da47fc5dde62b016bb6af49 files/apparmor-sources-2.6.17-r1-apparmor_main.patch 219968 +RMD160 8ac7a4ee65da4180684634b82bd468e2ff0ff790 files/apparmor-sources-2.6.17-r1-apparmor_main.patch 219968 +SHA256 f799f9f570dd1de355aad61a3583a6e06aba6138f4b7c064274ad524d4c6ab18 files/apparmor-sources-2.6.17-r1-apparmor_main.patch 219968 +AUX apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch 1197 RMD160 99acdb25d6384f650edb2914fb05205abe2a4f48 SHA1 21ee82c1bc2089f8af1985fcc57049422f5e02ba SHA256 1c6c97d3ef47c3b21e335246fabc4dbdb446e5fd638250a928ed62c8277ac015 +MD5 ebc4034d4b5410191a67a3d5f2e7d82b files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch 1197 +RMD160 99acdb25d6384f650edb2914fb05205abe2a4f48 files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch 1197 +SHA256 1c6c97d3ef47c3b21e335246fabc4dbdb446e5fd638250a928ed62c8277ac015 files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch 1197 +DIST genpatches-2.6.17-1.base.tar.bz2 3337 RMD160 f9b5621fed8fcfee7da697d89097842287b41b24 SHA1 308e3daec11899f03103b5dcd2bfcff7e116c52c SHA256 2208b72729dce6daef7dc5700192aec0ae17327c794681621d2123f0c483ae21 +DIST genpatches-2.6.17-1.extras.tar.bz2 138704 RMD160 14b17e02b7893e6b023bee9e1b40f4bc85a30f05 SHA1 e671dbc29239116e627aea3d87ff7aefd8b5ca00 SHA256 fa10ec7d3d74c8bf57fb3bd01c33f83dcca3c1e4cd4601937cc2ef904cce1dfe +DIST linux-2.6.17.tar.bz2 41272919 RMD160 26aad30c9a6610665c6c7d62401d79bf56a6a699 SHA1 0605c975b9dead2af31a3decf09dd4138fadaf2b SHA256 ab0f647d52f124958439517df9e1ae0efda90cdb851f59f522fa1749f1d87d58 +EBUILD apparmor-sources-2.6.17-r1.ebuild 1142 RMD160 1a66dbec96105e2c33287e35839841abb431f6a3 SHA1 38f3b1e29e4d69babc6a9fc0667ad4048d7d09f1 SHA256 49ac1068e9ae0633574904d2f7a939a12916b19cc3a8909bbc8b3561f391ea12 +MD5 90cb28e5a5b500a6373f5ced5162526f apparmor-sources-2.6.17-r1.ebuild 1142 +RMD160 1a66dbec96105e2c33287e35839841abb431f6a3 apparmor-sources-2.6.17-r1.ebuild 1142 +SHA256 49ac1068e9ae0633574904d2f7a939a12916b19cc3a8909bbc8b3561f391ea12 apparmor-sources-2.6.17-r1.ebuild 1142 +EBUILD apparmor-sources-2.6.17.ebuild 977 RMD160 ac4b79962d1397e0402b8d77cfb0122847267487 SHA1 78a3d9b6a412f77ac498235e8cdfba3e2de5f420 SHA256 d2233bcc23eca9bbc84ffb74ed8ce93fee706a8a0f514dc3967e62e656bd46cd +MD5 0385b5fae689c746f3ddc97ac97809c1 apparmor-sources-2.6.17.ebuild 977 +RMD160 ac4b79962d1397e0402b8d77cfb0122847267487 apparmor-sources-2.6.17.ebuild 977 +SHA256 d2233bcc23eca9bbc84ffb74ed8ce93fee706a8a0f514dc3967e62e656bd46cd apparmor-sources-2.6.17.ebuild 977 +MD5 0e1ade0aa9228c7dad088db16e4f1d3a files/digest-apparmor-sources-2.6.17 801 +RMD160 74e1080cfdd78f0b7cc0314a2822efbe02ec2333 files/digest-apparmor-sources-2.6.17 801 +SHA256 482238abaafe1482e35b0de5d6fbde3cc5f3e307aa885f823693b5bf01edcc09 files/digest-apparmor-sources-2.6.17 801 +MD5 0e1ade0aa9228c7dad088db16e4f1d3a files/digest-apparmor-sources-2.6.17-r1 801 +RMD160 74e1080cfdd78f0b7cc0314a2822efbe02ec2333 files/digest-apparmor-sources-2.6.17-r1 801 +SHA256 482238abaafe1482e35b0de5d6fbde3cc5f3e307aa885f823693b5bf01edcc09 files/digest-apparmor-sources-2.6.17-r1 801 diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/apparmor-sources-2.6.17-r1.ebuild b/trunk/novell4gentoo/sys-kernel/apparmor-sources/apparmor-sources-2.6.17-r1.ebuild new file mode 100644 index 0000000..5c1e43f --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/apparmor-sources-2.6.17-r1.ebuild @@ -0,0 +1,43 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +ETYPE="sources" +K_WANT_GENPATCHES="base extras" +K_GENPATCHES_VER="1" +inherit eutils kernel-2 +detect_version +detect_arch + +KEYWORDS="~x86 ~amd64" +HOMEPAGE="http://forge.novell.com/modules/xfmod/project/?apparmor" +DESCRIPTION="Full sources to provide the required AppArmor modules and kernel hooks. Based on the gentoo-sources tree." + +SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI}" + +src_unpack() { + ABI=${KERNEL_ABI} + kernel-2_src_unpack + + EPATCH_FORCE="yes" + EPATCH_OPTS="" + epatch ${FILESDIR}/${PF}*.patch +} + +pkg_postinst() { + postinst_sources + + einfo + einfo "For more info on this patchset, and how to report problems, see:" + einfo "${HOMEPAGE}" + + einfo + einfo "Make sure that your kernel configuration file is set with:" + einfo " CONFIG_SECURITY=y" + einfo " CONFIG_SECURITY_APPARMOR=m" + einfo "Without these, apparmor will not function." + einfo + einfo "Apparmor can be sensitive to module load order. Make sure" + einfo "it is listed before any other modules that rely on the " + einfo "'capability' module." +} diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/apparmor-sources-2.6.17.ebuild b/trunk/novell4gentoo/sys-kernel/apparmor-sources/apparmor-sources-2.6.17.ebuild new file mode 100644 index 0000000..83c74c1 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/apparmor-sources-2.6.17.ebuild @@ -0,0 +1,39 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +ETYPE="sources" +K_WANT_GENPATCHES="base extras" +K_GENPATCHES_VER="1" +inherit eutils kernel-2 +detect_version +detect_arch + +KEYWORDS="~x86 ~amd64" +HOMEPAGE="http://forge.novell.com/modules/xfmod/project/?apparmor" +DESCRIPTION="Full sources to provide the required AppArmor modules and kernel hooks. Based on the gentoo-sources tree." + +SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI}" + +src_unpack() { + ABI=${KERNEL_ABI} + kernel-2_src_unpack + + EPATCH_FORCE="yes" + EPATCH_OPTS="" + epatch ${FILESDIR}/*_apparmor-*.patch +} + +pkg_postinst() { + postinst_sources + + einfo + einfo "For more info on this patchset, and how to report problems, see:" + einfo "${HOMEPAGE}" + + einfo + einfo "Make sure that your kernel configuration file is set with:" + einfo " CONFIG_SECURITY=y" + einfo " CONFIG_SECURITY_APPARMOR=m" + einfo "Without these, apparmor will not function." +} diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5000_apparmor-integrate.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5000_apparmor-integrate.patch new file mode 100644 index 0000000..f729072 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5000_apparmor-integrate.patch @@ -0,0 +1,76 @@ +This patch glues AppArmor into the security configuration and Makefile. +It also creates the AppArmor configuration and Makefile. + + +Signed-off-by: Tony Jones + +--- + MAINTAINERS | 7 +++++++ + security/Kconfig | 1 + + security/Makefile | 1 + + security/apparmor/Kconfig | 9 +++++++++ + security/apparmor/Makefile | 6 ++++++ + 5 files changed, 24 insertions(+) + +--- linux-2.6.17-rc1.orig/MAINTAINERS ++++ linux-2.6.17-rc1/MAINTAINERS +@@ -284,6 +284,13 @@ + W: http://www.canb.auug.org.au/~sfr/ + S: Supported + ++APPARMOR SECURITY MODULE ++P: Tony Jones ++M: tonyj@suse.de ++L: apparmor-dev@forge.novell.com ++W: http://forge.novell.com/modules/xfmod/project/?apparmor ++S: Supported ++ + APPLETALK NETWORK LAYER + P: Arnaldo Carvalho de Melo + M: acme@conectiva.com.br +--- linux-2.6.17-rc1.orig/security/Kconfig ++++ linux-2.6.17-rc1/security/Kconfig +@@ -100,6 +100,7 @@ + If you are unsure how to answer this question, answer N. + + source security/selinux/Kconfig ++source security/apparmor/Kconfig + + endmenu + +--- linux-2.6.17-rc1.orig/security/Makefile ++++ linux-2.6.17-rc1/security/Makefile +@@ -4,6 +4,7 @@ + + obj-$(CONFIG_KEYS) += keys/ + subdir-$(CONFIG_SECURITY_SELINUX) += selinux ++subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor + + # if we don't select a security model, use the default capabilities + ifneq ($(CONFIG_SECURITY),y) +--- /dev/null ++++ linux-2.6.17-rc1/security/apparmor/Kconfig +@@ -0,0 +1,9 @@ ++config SECURITY_APPARMOR ++ tristate "AppArmor support" ++ depends on SECURITY!=n ++ help ++ This enables the AppArmor security module. ++ Required userspace tools (if they are not included in your ++ distribution) and further information may be found at ++ ++ If you are unsure how to answer this question, answer N. +--- /dev/null ++++ linux-2.6.17-rc1/security/apparmor/Makefile +@@ -0,0 +1,6 @@ ++# Makefile for AppArmor Linux Security Module ++# ++subdir-$(CONFIG_SECURITY_APPARMOR) += match ++obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o ++ ++apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o capabilities.o module_interface.o +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5001_apparmor-core-header.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5001_apparmor-core-header.patch new file mode 100644 index 0000000..3bd74be --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5001_apparmor-core-header.patch @@ -0,0 +1,728 @@ +This patch provides the various common headerfiles used by the AppArmor module. + +apparmor.h contains the core data structures. +shared.h contains definitions that are common to the userspace policy loader. +inline.h implements various inline utility functions + + +Signed-off-by: Tony Jones + +--- + security/apparmor/apparmor.h | 325 +++++++++++++++++++++++++++++++++++++++++ + security/apparmor/inline.h | 333 +++++++++++++++++++++++++++++++++++++++++++ + security/apparmor/shared.h | 41 +++++ + 3 files changed, 699 insertions(+) + +--- security/apparmor/apparmor.h.orig ++++ security/apparmor/apparmor.h +@@ -0,0 +1,325 @@ ++/* ++ * Copyright (C) 1998-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor internal prototypes ++ */ ++ ++#ifndef __SUBDOMAIN_H ++#define __SUBDOMAIN_H ++ ++#include /* Include for defn of iattr */ ++#include ++ ++#include "shared.h" ++ ++/* Control parameters (0 or 1), settable thru module/boot flags or ++ * via /sys/kernel/security/apparmor/control */ ++extern int apparmor_complain; ++extern int apparmor_debug; ++extern int apparmor_audit; ++extern int apparmor_logsyscall; ++ ++/* PIPEFS_MAGIC */ ++#include ++/* from net/socket.c */ ++#define SOCKFS_MAGIC 0x534F434B ++/* from inotify.c */ ++#define INOTIFYFS_MAGIC 0xBAD1DEA ++ ++#define VALID_FSTYPE(inode) ((inode)->i_sb->s_magic != PIPEFS_MAGIC && \ ++ (inode)->i_sb->s_magic != SOCKFS_MAGIC && \ ++ (inode)->i_sb->s_magic != INOTIFYFS_MAGIC) ++ ++#define PROFILE_COMPLAIN(_profile) \ ++ (apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain)) ++ ++#define SUBDOMAIN_COMPLAIN(_sd) \ ++ (apparmor_complain == 1 || \ ++ ((_sd) && (_sd)->active && (_sd)->active->flags.complain)) ++ ++#define PROFILE_AUDIT(_profile) \ ++ (apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit)) ++ ++#define SUBDOMAIN_AUDIT(_sd) \ ++ (apparmor_audit == 1 || \ ++ ((_sd) && (_sd)->active && (_sd)->active->flags.audit)) ++ ++/* ++ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl ++ * which is not related to profile accesses. ++ */ ++ ++#define AA_DEBUG(fmt, args...) \ ++ do { \ ++ if (apparmor_debug) \ ++ printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ ++ } while (0) ++#define AA_INFO(fmt, args...) printk(KERN_INFO "AppArmor: " fmt, ##args) ++#define AA_WARN(fmt, args...) printk(KERN_WARNING "AppArmor: " fmt, ##args) ++#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args) ++ ++/* basic AppArmor data structures */ ++ ++struct flagval { ++ int debug; ++ int complain; ++ int audit; ++}; ++ ++enum entry_match_type { ++ aa_entry_literal, ++ aa_entry_tailglob, ++ aa_entry_pattern, ++ aa_entry_invalid ++}; ++ ++/* struct aa_entry - file ACL * ++ * @filename: filename controlled by this ACL ++ * @mode: permissions granted by ACL ++ * @type: type of match to perform against @filename ++ * @extradata: any extra data needed by an extended matching type ++ * @list: list the ACL is on ++ * @listp: permission partitioned lists this ACL is on. ++ * ++ * Each entry describes a file and an allowed access mode. ++ */ ++struct aa_entry { ++ char *filename; ++ int mode; /* mode is 'or' of READ, WRITE, EXECUTE, ++ * INHERIT, UNCONSTRAINED, and LIBRARY ++ * (meaning don't prefetch). */ ++ ++ enum entry_match_type type; ++ void *extradata; ++ ++ struct list_head list; ++ struct list_head listp[POS_AA_FILE_MAX + 1]; ++}; ++ ++#define AA_EXEC_MODIFIER_MASK(mask) ((mask) & (AA_EXEC_UNCONSTRAINED |\ ++ AA_EXEC_INHERIT |\ ++ AA_EXEC_PROFILE)) ++ ++#define AA_EXEC_MASK(mask) ((mask) & (AA_MAY_EXEC |\ ++ AA_EXEC_UNCONSTRAINED |\ ++ AA_EXEC_INHERIT |\ ++ AA_EXEC_PROFILE)) ++ ++ ++/* struct aaprofile - basic confinement data ++ * @parent: non refcounted pointer to parent profile ++ * @name: the profiles name ++ * @file_entry: file ACL ++ * @file_entryp: vector of file ACL by permission granted ++ * @list: list this profile is on ++ * @sub: profiles list of subprofiles (HATS) ++ * @flags: flags controlling profile behavior ++ * @null_profile: if needed per profile learning and null confinement profile ++ * @isstale: flag to indicate the profile is stale ++ * @num_file_entries: number of file entries the profile contains ++ * @num_file_pentries: number of file entries for each partitioned list ++ * @capabilities: capabilities granted by the process ++ * @rcu: rcu head used when freeing the profile ++ * @count: reference count of the profile ++ * ++ * The AppArmor profile contains the basic confinement data. Each profile ++ * has a name and potentially a list of profile entries. The profiles are ++ * connected in a list ++ */ ++struct aaprofile { ++ struct aaprofile *parent; ++ char *name; ++ ++ struct list_head file_entry; ++ struct list_head file_entryp[POS_AA_FILE_MAX + 1]; ++ struct list_head list; ++ struct list_head sub; ++ struct flagval flags; ++ struct aaprofile *null_profile; ++ int isstale; ++ ++ int num_file_entries; ++ int num_file_pentries[POS_AA_FILE_MAX + 1]; ++ ++ kernel_cap_t capabilities; ++ ++ struct rcu_head rcu; ++ ++ struct kref count; ++}; ++ ++/** ++ * struct subdomain - primary label for confined tasks ++ * @active: the current active profile ++ * @hat_magic: the magic token controling the ability to leave a hat ++ * @list: list this subdomain is on ++ * @task: task that the subdomain confines ++ * ++ * Contains the tasks current active profile (which could change due to ++ * change_hat). Plus the hat_magic needed during change_hat. ++ * ++ * N.B AppArmor's previous product name SubDomain was derived from the name ++ * of this structure/concept (changehat reducing a task into a sub-domain). ++ */ ++struct subdomain { ++ struct aaprofile *active; /* The current active profile */ ++ u32 hat_magic; /* used with change_hat */ ++ struct list_head list; /* list of subdomains */ ++ struct task_struct *task; ++}; ++ ++typedef int (*aa_iter) (struct subdomain *, void *); ++ ++/* aa_path_data ++ * temp (cookie) data used by aa_path_* functions, see inline.h ++ */ ++struct aa_path_data { ++ struct dentry *root, *dentry; ++ struct namespace *namespace; ++ struct list_head *head, *pos; ++ int errno; ++}; ++ ++#define AA_SUBDOMAIN(sec) ((struct subdomain*)(sec)) ++#define AA_PROFILE(sec) ((struct aaprofile*)(sec)) ++ ++/* Lock protecting access to 'struct subdomain' accesses */ ++extern spinlock_t sd_lock; ++ ++extern struct aaprofile *null_complain_profile; ++ ++/* aa_audit - AppArmor auditing structure ++ * Structure is populated by access control code and passed to aa_audit which ++ * provides for a single point of logging. ++ */ ++ ++struct aa_audit { ++ unsigned short type, flags; ++ unsigned int result; ++ unsigned int gfp_mask; ++ int error_code; ++ ++ const char *name; ++ unsigned int ival; ++ union { ++ const void *pval; ++ va_list vaval; ++ }; ++}; ++ ++/* audit types */ ++#define AA_AUDITTYPE_FILE 1 ++#define AA_AUDITTYPE_DIR 2 ++#define AA_AUDITTYPE_ATTR 3 ++#define AA_AUDITTYPE_XATTR 4 ++#define AA_AUDITTYPE_LINK 5 ++#define AA_AUDITTYPE_CAP 6 ++#define AA_AUDITTYPE_MSG 7 ++#define AA_AUDITTYPE_SYSCALL 8 ++#define AA_AUDITTYPE__END 9 ++ ++/* audit flags */ ++#define AA_AUDITFLAG_AUDITSS_SYSCALL 1 /* log syscall context */ ++#define AA_AUDITFLAG_LOGERR 2 /* log operations that failed due to ++ non permission errors */ ++ ++#define HINT_UNKNOWN_HAT "unknown_hat" ++#define HINT_FORK "fork" ++#define HINT_MANDPROF "missing_mandatory_profile" ++#define HINT_CHGPROF "changing_profile" ++ ++#define LOG_HINT(p, gfp, hint, fmt, args...) \ ++ do {\ ++ aa_audit_message(p, gfp, 0, \ ++ "LOGPROF-HINT " hint " " fmt, ##args);\ ++ } while(0) ++ ++/* directory op type, for aa_perm_dir */ ++enum aa_diroptype { ++ aa_dir_mkdir, ++ aa_dir_rmdir ++}; ++ ++/* xattr op type, for aa_xattr */ ++enum aa_xattroptype { ++ aa_xattr_get, ++ aa_xattr_set, ++ aa_xattr_list, ++ aa_xattr_remove ++}; ++ ++#define BASE_PROFILE(p) ((p)->parent ? (p)->parent : (p)) ++#define IN_SUBPROFILE(p) ((p)->parent) ++ ++/* main.c */ ++extern int alloc_null_complain_profile(void); ++extern void free_null_complain_profile(void); ++extern int attach_nullprofile(struct aaprofile *profile); ++extern int aa_audit_message(struct aaprofile *active, unsigned int gfp, int, ++ const char *, ...); ++extern int aa_audit_syscallreject(struct aaprofile *active, unsigned int gfp, ++ const char *); ++extern int aa_audit(struct aaprofile *active, const struct aa_audit *); ++extern char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt); ++ ++extern int aa_attr(struct aaprofile *active, struct dentry *dentry, ++ struct iattr *iattr); ++extern int aa_xattr(struct aaprofile *active, struct dentry *dentry, ++ const char *xattr, enum aa_xattroptype xattroptype); ++extern int aa_capability(struct aaprofile *active, int cap); ++extern int aa_perm(struct aaprofile *active, struct dentry *dentry, ++ struct vfsmount *mnt, int mask); ++extern int aa_perm_nameidata(struct aaprofile *active, struct nameidata *nd, ++ int mask); ++extern int aa_perm_dentry(struct aaprofile *active, struct dentry *dentry, ++ int mask); ++extern int aa_perm_dir(struct aaprofile *active, struct dentry *dentry, ++ enum aa_diroptype diroptype); ++extern int aa_link(struct aaprofile *active, ++ struct dentry *link, struct dentry *target); ++extern int aa_fork(struct task_struct *p); ++extern int aa_register(struct file *file); ++extern void aa_release(struct task_struct *p); ++extern int aa_change_hat(const char *id, u32 hat_magic); ++extern int aa_associate_filp(struct file *filp); ++ ++/* list.c */ ++extern struct aaprofile *aa_profilelist_find(const char *name); ++extern int aa_profilelist_add(struct aaprofile *profile); ++extern struct aaprofile *aa_profilelist_remove(const char *name); ++extern void aa_profilelist_release(void); ++extern struct aaprofile *aa_profilelist_replace(struct aaprofile *profile); ++extern void aa_profile_dump(struct aaprofile *); ++extern void aa_profilelist_dump(void); ++extern void aa_subdomainlist_add(struct subdomain *); ++extern void aa_subdomainlist_remove(struct subdomain *); ++extern void aa_subdomainlist_iterate(aa_iter, void *); ++extern void aa_subdomainlist_iterateremove(aa_iter, void *); ++extern void aa_subdomainlist_release(void); ++ ++/* module_interface.c */ ++extern ssize_t aa_file_prof_add(void *, size_t); ++extern ssize_t aa_file_prof_repl(void *, size_t); ++extern ssize_t aa_file_prof_remove(const char *, size_t); ++extern void free_aaprofile(struct aaprofile *profile); ++extern void free_aaprofile_kref(struct kref *kref); ++ ++/* procattr.c */ ++extern size_t aa_getprocattr(struct aaprofile *active, char *str, size_t size); ++extern int aa_setprocattr_changehat(char *hatinfo, size_t infosize); ++extern int aa_setprocattr_setprofile(struct task_struct *p, char *profilename, ++ size_t profilesize); ++ ++/* apparmorfs.c */ ++extern int create_apparmorfs(void); ++extern void destroy_apparmorfs(void); ++ ++/* capabilities.c */ ++extern const char *capability_to_name(unsigned int cap); ++ ++#endif /* __SUBDOMAIN_H */ +--- security/apparmor/inline.h.orig ++++ security/apparmor/inline.h +@@ -0,0 +1,333 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef __INLINE_H ++#define __INLINE_H ++ ++#include ++ ++static inline int __aa_is_confined(struct subdomain *sd) ++{ ++ return (sd && sd->active); ++} ++ ++/** ++ * aa_is_confined ++ * Determine whether current task contains a valid profile (confined). ++ * Return %1 if confined, %0 otherwise. ++ */ ++static inline int aa_is_confined(void) ++{ ++ struct subdomain *sd = AA_SUBDOMAIN(current->security); ++ return __aa_is_confined(sd); ++} ++ ++static inline int __aa_sub_defined(struct subdomain *sd) ++{ ++ return __aa_is_confined(sd) && !list_empty(&BASE_PROFILE(sd->active)->sub); ++} ++ ++/** ++ * aa_sub_defined - check to see if current task has any subprofiles ++ * Return 1 if true, 0 otherwise ++ */ ++static inline int aa_sub_defined(void) ++{ ++ struct subdomain *sd = AA_SUBDOMAIN(current->security); ++ return __aa_sub_defined(sd); ++} ++ ++/** ++ * get_aaprofile - increment refcount on profile @p ++ * @p: profile ++ */ ++static inline struct aaprofile *get_aaprofile(struct aaprofile *p) ++{ ++ if (p) ++ kref_get(&(BASE_PROFILE(p)->count)); ++ ++ return p; ++} ++ ++/** ++ * put_aaprofile - decrement refcount on profile @p ++ * @p: profile ++ */ ++static inline void put_aaprofile(struct aaprofile *p) ++{ ++ if (p) ++ kref_put(&BASE_PROFILE(p)->count, free_aaprofile_kref); ++} ++ ++/** ++ * get_task_activeptr_rcu - get pointer to @tsk's active profile. ++ * @tsk: task to get active profile from ++ * ++ * Requires rcu_read_lock is held ++ */ ++static inline struct aaprofile *get_task_activeptr_rcu(struct task_struct *tsk) ++{ ++ struct subdomain *sd = AA_SUBDOMAIN(tsk->security); ++ struct aaprofile *active = NULL; ++ ++ if (sd) ++ active = (struct aaprofile *) rcu_dereference(sd->active); ++ ++ return active; ++} ++ ++/** ++ * get_activeptr_rcu - get pointer to current task's active profile ++ * Requires rcu_read_lock is held ++ */ ++static inline struct aaprofile *get_activeptr_rcu(void) ++{ ++ return get_task_activeptr_rcu(current); ++} ++ ++/** ++ * get_task_active_aaprofile - get a reference to tsk's active profile. ++ * @tsk: the task to get the active profile reference for ++ */ ++static inline struct aaprofile *get_task_active_aaprofile(struct task_struct *tsk) ++{ ++ struct aaprofile *active; ++ ++ rcu_read_lock(); ++ active = get_aaprofile(get_task_activeptr_rcu(tsk)); ++ rcu_read_unlock(); ++ ++ return active; ++} ++ ++/** ++ * get_active_aaprofile - get a reference to the current tasks active profile ++ */ ++static inline struct aaprofile *get_active_aaprofile(void) ++{ ++ return get_task_active_aaprofile(current); ++} ++ ++/** ++ * aa_switch - change subdomain to use a new profile ++ * @sd: subdomain to switch the active profile on ++ * @newactive: new active profile ++ * ++ * aa_switch handles the changing of a subdomain's active profile. The ++ * sd_lock must be held to ensure consistency against other writers. ++ * Some write paths (ex. aa_register) require sd->active not to change ++ * over several operations, so the calling function is responsible ++ * for grabing the sd_lock to meet its consistency constraints before ++ * calling aa_switch ++ */ ++static inline void aa_switch(struct subdomain *sd, struct aaprofile *newactive) ++{ ++ struct aaprofile *oldactive = sd->active; ++ ++ /* noop if NULL */ ++ rcu_assign_pointer(sd->active, get_aaprofile(newactive)); ++ put_aaprofile(oldactive); ++} ++ ++/** ++ * aa_switch_unconfined - change subdomain to be unconfined (no profile) ++ * @sd: subdomain to switch ++ * ++ * aa_switch_unconfined handles the removal of a subdomain's active profile. ++ * The sd_lock must be held to ensure consistency against other writers. ++ * Like aa_switch the sd_lock is used to maintain consistency. ++ */ ++static inline void aa_switch_unconfined(struct subdomain *sd) ++{ ++ aa_switch(sd, NULL); ++ ++ /* reset magic in case we were in a subhat before */ ++ sd->hat_magic = 0; ++} ++ ++/** ++ * alloc_subdomain - allocate a new subdomain ++ * @tsk: task struct ++ * ++ * Allocate a new subdomain including a backpointer to it's referring task. ++ */ ++static inline struct subdomain *alloc_subdomain(struct task_struct *tsk) ++{ ++ struct subdomain *sd; ++ ++ sd = kzalloc(sizeof(struct subdomain), GFP_KERNEL); ++ if (!sd) ++ goto out; ++ ++ /* back pointer to task */ ++ sd->task = tsk; ++ ++ /* any readers of the list must make sure that they can handle ++ * case where sd->active is not yet set (null) ++ */ ++ aa_subdomainlist_add(sd); ++ ++out: ++ return sd; ++} ++ ++/** ++ * free_subdomain - Free a subdomain previously allocated by alloc_subdomain ++ * @sd: subdomain ++ */ ++static inline void free_subdomain(struct subdomain *sd) ++{ ++ aa_subdomainlist_remove(sd); ++ kfree(sd); ++} ++ ++/** ++ * alloc_aaprofile - Allocate, initialize and return a new zeroed profile. ++ * Returns NULL on failure. ++ */ ++static inline struct aaprofile *alloc_aaprofile(void) ++{ ++ struct aaprofile *profile; ++ ++ profile = (struct aaprofile *)kzalloc(sizeof(struct aaprofile), ++ GFP_KERNEL); ++ AA_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ if (profile) { ++ int i; ++ ++ INIT_LIST_HEAD(&profile->list); ++ INIT_LIST_HEAD(&profile->sub); ++ INIT_LIST_HEAD(&profile->file_entry); ++ for (i = 0; i <= POS_AA_FILE_MAX; i++) { ++ INIT_LIST_HEAD(&profile->file_entryp[i]); ++ } ++ INIT_RCU_HEAD(&profile->rcu); ++ kref_init(&profile->count); ++ } ++ return profile; ++} ++ ++/** ++ * aa_put_name ++ * @name: name to release. ++ * ++ * Release space (free_page) allocated to hold pathname ++ * name may be NULL (checked for by free_page) ++ */ ++static inline void aa_put_name(const char *name) ++{ ++ free_page((unsigned long)name); ++} ++ ++/** __aa_find_profile ++ * @name: name of profile to find ++ * @head: list to search ++ * ++ * Return reference counted copy of profile. NULL if not found ++ * Caller must hold any necessary locks ++ */ ++static inline struct aaprofile *__aa_find_profile(const char *name, ++ struct list_head *head) ++{ ++ struct aaprofile *p; ++ ++ if (!name || !head) ++ return NULL; ++ ++ AA_DEBUG("%s: finding profile %s\n", __FUNCTION__, name); ++ list_for_each_entry(p, head, list) { ++ if (!strcmp(p->name, name)) { ++ /* return refcounted object */ ++ p = get_aaprofile(p); ++ return p; ++ } else { ++ AA_DEBUG("%s: skipping %s\n", __FUNCTION__, p->name); ++ } ++ } ++ return NULL; ++} ++ ++/** __aa_path_begin ++ * @rdentry: filesystem root dentry (searching for vfsmnts matching this) ++ * @dentry: dentry object to obtain pathname from (relative to matched vfsmnt) ++ * ++ * Setup data for iterating over vfsmounts (in current tasks namespace). ++ */ ++static inline void __aa_path_begin(struct dentry *rdentry, ++ struct dentry *dentry, ++ struct aa_path_data *data) ++{ ++ data->dentry = dentry; ++ data->root = dget(rdentry->d_sb->s_root); ++ data->namespace = current->namespace; ++ data->head = &data->namespace->list; ++ data->pos = data->head->next; ++ prefetch(data->pos->next); ++ data->errno = 0; ++ ++ down_read(&namespace_sem); ++} ++ ++/** aa_path_begin ++ * @dentry: filesystem root dentry and object to obtain pathname from ++ * ++ * Utility function for calling _aa_path_begin for when the dentry we are ++ * looking for and the root are the same (this is the usual case). ++ */ ++static inline void aa_path_begin(struct dentry *dentry, ++ struct aa_path_data *data) ++{ ++ __aa_path_begin(dentry, dentry, data); ++} ++ ++/** aa_path_end ++ * @data: data object previously initialized by aa_path_begin ++ * ++ * End iterating over vfsmounts. ++ * If an error occured in begin or get, it is returned. Otherwise 0. ++ */ ++static inline int aa_path_end(struct aa_path_data *data) ++{ ++ up_read(&namespace_sem); ++ dput(data->root); ++ ++ return data->errno; ++} ++ ++/** aa_path_getname ++ * @data: data object previously initialized by aa_path_begin ++ * ++ * Return the next mountpoint which has the same root dentry as data->root. ++ * If no more mount points exist (or in case of error) NULL is returned ++ * (caller should call aa_path_end() and inspect return code to differentiate) ++ */ ++static inline char *aa_path_getname(struct aa_path_data *data) ++{ ++ char *name = NULL; ++ struct vfsmount *mnt; ++ ++ while (data->pos != data->head) { ++ mnt = list_entry(data->pos, struct vfsmount, mnt_list); ++ ++ /* advance to next -- so that it is done before we break */ ++ data->pos = data->pos->next; ++ prefetch(data->pos->next); ++ ++ if (mnt->mnt_root == data->root) { ++ name = aa_get_name(data->dentry, mnt); ++ if (!name) ++ data->errno = -ENOMEM; ++ break; ++ } ++ } ++ ++ return name; ++} ++ ++#endif /* __INLINE_H__ */ +--- security/apparmor/shared.h.orig ++++ security/apparmor/shared.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (C) 2000, 2001, 2004, 2005 Novell/SUSE ++ * ++ * Immunix AppArmor LSM ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef _SHARED_H ++#define _SHARED_H ++ ++/* start of system offsets */ ++#define POS_AA_FILE_MIN 0 ++#define POS_AA_MAY_EXEC POS_AA_FILE_MIN ++#define POS_AA_MAY_WRITE (POS_AA_MAY_EXEC + 1) ++#define POS_AA_MAY_READ (POS_AA_MAY_WRITE + 1) ++#define POS_AA_MAY_APPEND (POS_AA_MAY_READ + 1) ++/* end of system offsets */ ++ ++#define POS_AA_MAY_LINK (POS_AA_MAY_APPEND + 1) ++#define POS_AA_EXEC_INHERIT (POS_AA_MAY_LINK + 1) ++#define POS_AA_EXEC_UNCONSTRAINED (POS_AA_EXEC_INHERIT + 1) ++#define POS_AA_EXEC_PROFILE (POS_AA_EXEC_UNCONSTRAINED + 1) ++#define POS_AA_FILE_MAX POS_AA_EXEC_PROFILE ++ ++/* Modeled after MAY_READ, MAY_WRITE, MAY_EXEC def'ns */ ++#define AA_MAY_EXEC (0x01 << POS_AA_MAY_EXEC) ++#define AA_MAY_WRITE (0x01 << POS_AA_MAY_WRITE) ++#define AA_MAY_READ (0x01 << POS_AA_MAY_READ) ++#define AA_MAY_LINK (0x01 << POS_AA_MAY_LINK) ++#define AA_EXEC_INHERIT (0x01 << POS_AA_EXEC_INHERIT) ++#define AA_EXEC_UNCONSTRAINED (0x01 << POS_AA_EXEC_UNCONSTRAINED) ++#define AA_EXEC_PROFILE (0x01 << POS_AA_EXEC_PROFILE) ++#define AA_EXEC_MODIFIERS(X) (X & (AA_EXEC_INHERIT | \ ++ A_EXEC_UNCONSTRAINED | \ ++ AA_EXEC_PROFILE)) ++ ++#endif /* _SHARED_H */ +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5002_apparmor-lsm-interface.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5002_apparmor-lsm-interface.patch new file mode 100644 index 0000000..d4b6617 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5002_apparmor-lsm-interface.patch @@ -0,0 +1,880 @@ +Implements the lsm interface used by AppArmor. + +The code composes the functionality provided by commoncap therefore there +is no requirement for it to stack with the capability module. + +See linux/include/security.h for a full description of all the LSM hooks. + +Consistency of the subdomain (task) data is implemented on the reader side +via rcu. Logical consistency across profile replacement/removal and change +hat is provided via the spinlock sd_lock. Since profile manipulation and +change_hat are infrequent, most syscall accesses requires no spin lock. + +Certain syscalls are prevented for confined processes. These are: + ptrace + mount + umount + sysctl writes also require CAP_SYS_ADMIN + +File access checks are performed when a file is initially opened +(inode_permission) and cached to avoid revalidation unless where necessary +(passing descriptors between tasks confined with differing profiles, and +profile replacement, for example). Further patches are in development +to support caching of multiple profiles against an open file to minimise +the need for subsequent revalidation across profiles. + + +Signed-off-by: Tony Jones + +--- + security/apparmor/lsm.c | 840 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 840 insertions(+) + +--- security/apparmor/lsm.c.orig ++++ security/apparmor/lsm.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * http://forge.novell.com/modules/xfmod/project/?apparmor ++ * ++ * Immunix AppArmor LSM interface ++ */ ++ ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++/* struct subdomain write update lock (read side is RCU). */ ++spinlock_t sd_lock = SPIN_LOCK_UNLOCKED; ++ ++/* Flag values, also controllable via apparmorfs/control. ++ * We explicitly do not allow these to be modifiable when exported via ++ * /sys/modules/parameters, as we want to do additional mediation and ++ * don't want to add special path code. */ ++ ++/* Complain mode -- in complain mode access failures result in auditing only ++ * and task is allowed access. audit events are processed by userspace to ++ * generate policy. Default is 'enforce' (0). ++ * Value is also togglable per profile and referenced when global value is ++ * enforce. ++ */ ++int apparmor_complain = 0; ++module_param_named(complain, apparmor_complain, int, S_IRUSR); ++MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode"); ++ ++/* Debug mode */ ++int apparmor_debug = 0; ++module_param_named(debug, apparmor_debug, int, S_IRUSR); ++MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode"); ++ ++/* Audit mode */ ++int apparmor_audit = 0; ++module_param_named(audit, apparmor_audit, int, S_IRUSR); ++MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode"); ++ ++/* Syscall logging mode */ ++int apparmor_logsyscall = 0; ++module_param_named(logsyscall, apparmor_logsyscall, int, S_IRUSR); ++MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode"); ++ ++#ifndef MODULE ++static int __init aa_getopt_complain(char *str) ++{ ++ get_option(&str, &apparmor_complain); ++ return 1; ++} ++__setup("apparmor_complain=", aa_getopt_complain); ++ ++static int __init aa_getopt_debug(char *str) ++{ ++ get_option(&str, &apparmor_debug); ++ return 1; ++} ++__setup("apparmor_debug=", aa_getopt_debug); ++ ++static int __init aa_getopt_audit(char *str) ++{ ++ get_option(&str, &apparmor_audit); ++ return 1; ++} ++__setup("apparmor_audit=", aa_getopt_audit); ++ ++static int __init aa_getopt_logsyscall(char *str) ++{ ++ get_option(&str, &apparmor_logsyscall); ++ return 1; ++} ++__setup("apparmor_logsyscall=", aa_getopt_logsyscall); ++#endif ++ ++static int apparmor_ptrace(struct task_struct *parent, ++ struct task_struct *child) ++{ ++ int error; ++ struct aaprofile *active; ++ ++ error = cap_ptrace(parent, child); ++ ++ active = get_active_aaprofile(); ++ ++ if (!error && active) { ++ error = aa_audit_syscallreject(active, GFP_KERNEL, "ptrace"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_capget(struct task_struct *target, ++ kernel_cap_t *effective, ++ kernel_cap_t *inheritable, ++ kernel_cap_t *permitted) ++{ ++ return cap_capget(target, effective, inheritable, permitted); ++} ++ ++static int apparmor_capset_check(struct task_struct *target, ++ kernel_cap_t *effective, ++ kernel_cap_t *inheritable, ++ kernel_cap_t *permitted) ++{ ++ return cap_capset_check(target, effective, inheritable, permitted); ++} ++ ++static void apparmor_capset_set(struct task_struct *target, ++ kernel_cap_t *effective, ++ kernel_cap_t *inheritable, ++ kernel_cap_t *permitted) ++{ ++ cap_capset_set(target, effective, inheritable, permitted); ++ return; ++} ++ ++static int apparmor_capable(struct task_struct *tsk, int cap) ++{ ++ int error; ++ ++ /* cap_capable returns 0 on success, else -EPERM */ ++ error = cap_capable(tsk, cap); ++ ++ if (error == 0) { ++ struct aaprofile *active; ++ ++ active = get_task_active_aaprofile(tsk); ++ ++ if (active) ++ error = aa_capability(active, cap); ++ ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++ ++static int apparmor_sysctl(struct ctl_table *table, int op) ++{ ++ int error = 0; ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ ++ if ((op & 002) && active && !capable(CAP_SYS_ADMIN)) { ++ error = aa_audit_syscallreject(active, GFP_KERNEL, ++ "sysctl (write)"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_syslog(int type) ++{ ++ return cap_syslog(type); ++} ++ ++static int apparmor_netlink_send(struct sock *sk, struct sk_buff *skb) ++{ ++ return cap_netlink_send(sk, skb); ++} ++ ++static int apparmor_netlink_recv(struct sk_buff *skb) ++{ ++ return cap_netlink_recv(skb); ++} ++ ++static void apparmor_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) ++{ ++ cap_bprm_apply_creds(bprm, unsafe); ++ return; ++} ++ ++static int apparmor_bprm_set_security(struct linux_binprm *bprm) ++{ ++ /* handle capability bits with setuid, etc */ ++ cap_bprm_set_security(bprm); ++ /* already set based on script name */ ++ if (bprm->sh_bang) ++ return 0; ++ return aa_register(bprm->file); ++} ++ ++static int apparmor_sb_mount(char *dev_name, struct nameidata *nd, char *type, ++ unsigned long flags, void *data) ++{ ++ int error = 0; ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) { ++ error = aa_audit_syscallreject(active, GFP_KERNEL, "mount"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_umount(struct vfsmount *mnt, int flags) ++{ ++ int error = 0; ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) { ++ error = aa_audit_syscallreject(active, GFP_KERNEL, "umount"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_mkdir(struct inode *inode, struct dentry *dentry, ++ int mask) ++{ ++ struct aaprofile *active; ++ int error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) ++ error = aa_perm_dir(active, dentry, aa_dir_mkdir); ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_rmdir(struct inode *inode, struct dentry *dentry) ++{ ++ struct aaprofile *active; ++ int error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) ++ error = aa_perm_dir(active, dentry, aa_dir_rmdir); ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_create(struct inode *inode, struct dentry *dentry, ++ int mask) ++{ ++ struct aaprofile *active; ++ int error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ /* At a minimum, need write perm to create */ ++ if (active) ++ error = aa_perm_dentry(active, dentry, MAY_WRITE); ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_link(struct dentry *old_dentry, struct inode *inode, ++ struct dentry *new_dentry) ++{ ++ int error = 0; ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) ++ error = aa_link(active, new_dentry, old_dentry); ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_unlink(struct inode *inode, struct dentry *dentry) ++{ ++ struct aaprofile *active; ++ int error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) ++ error = aa_perm_dentry(active, dentry, MAY_WRITE); ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_mknod(struct inode *inode, struct dentry *dentry, ++ int mode, dev_t dev) ++{ ++ struct aaprofile *active; ++ int error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) ++ error = aa_perm_dentry(active, dentry, MAY_WRITE); ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_rename(struct inode *old_inode, ++ struct dentry *old_dentry, ++ struct inode *new_inode, ++ struct dentry *new_dentry) ++{ ++ struct aaprofile *active; ++ int error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ if (active) { ++ error = aa_perm_dentry(active, old_dentry, MAY_READ | ++ MAY_WRITE); ++ ++ if (!error) ++ error = aa_perm_dentry(active, new_dentry, ++ MAY_WRITE); ++ } ++ ++ put_aaprofile(active); ++ ++ return error; ++} ++ ++static int apparmor_inode_permission(struct inode *inode, int mask, ++ struct nameidata *nd) ++{ ++ int error = 0; ++ ++ /* Do not perform check on pipes or sockets ++ * Same as apparmor_file_permission ++ */ ++ if (VALID_FSTYPE(inode)) { ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ if (active) ++ error = aa_perm_nameidata(active, nd, mask); ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++ ++static int apparmor_inode_setattr(struct dentry *dentry, struct iattr *iattr) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ /* ++ * Mediate any attempt to change attributes of a file ++ * (chmod, chown, chgrp, etc) ++ */ ++ if (active) ++ error = aa_attr(active, dentry, iattr); ++ ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++ ++static int apparmor_inode_setxattr(struct dentry *dentry, char *name, ++ void *value, size_t size, int flags) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ if (active) ++ error = aa_xattr(active, dentry, name, aa_xattr_set); ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++ ++static int apparmor_inode_getxattr(struct dentry *dentry, char *name) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ if (active) ++ error = aa_xattr(active, dentry, name, aa_xattr_get); ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++static int apparmor_inode_listxattr(struct dentry *dentry) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ if (active) ++ error = aa_xattr(active, dentry, NULL, aa_xattr_list); ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++ ++static int apparmor_inode_removexattr(struct dentry *dentry, char *name) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ if (active) ++ error = aa_xattr(active, dentry, name, ++ aa_xattr_remove); ++ put_aaprofile(active); ++ } ++ ++ return error; ++} ++ ++static int apparmor_file_permission(struct file *file, int mask) ++{ ++ struct aaprofile *active; ++ struct aaprofile *f_profile; ++ int error = 0; ++ ++ f_profile = AA_PROFILE(file->f_security); ++ /* bail out early if this isn't a mediated file */ ++ if (!(f_profile && VALID_FSTYPE(file->f_dentry->d_inode))) ++ goto out; ++ ++ active = get_active_aaprofile(); ++ if (active && f_profile != active) ++ error = aa_perm(active, file->f_dentry, file->f_vfsmnt, ++ mask & (MAY_EXEC | MAY_WRITE | MAY_READ)); ++ put_aaprofile(active); ++ ++out: ++ return error; ++} ++ ++static int apparmor_file_alloc_security(struct file *file) ++{ ++ struct aaprofile *active; ++ ++ active = get_active_aaprofile(); ++ file->f_security = get_aaprofile(active); ++ put_aaprofile(active); ++ ++ return 0; ++} ++ ++static void apparmor_file_free_security(struct file *file) ++{ ++ struct aaprofile *p = AA_PROFILE(file->f_security); ++ put_aaprofile(p); ++} ++ ++static int apparmor_file_mmap(struct file *file, unsigned long reqprot, ++ unsigned long prot, unsigned long flags) ++{ ++ int error = 0, mask = 0; ++ struct aaprofile *active; ++ ++ if (!file) ++ goto out; ++ ++ active = get_active_aaprofile(); ++ ++ if (prot & PROT_READ) ++ mask |= MAY_READ; ++ /* Private mappings don't require write perms since they don't ++ * write back to the files */ ++ if (prot & PROT_WRITE && !(flags & MAP_PRIVATE)) ++ mask |= MAY_WRITE; ++ if (prot & PROT_EXEC) ++ mask |= MAY_EXEC; ++ ++ AA_DEBUG("%s: 0x%x\n", __FUNCTION__, mask); ++ ++ error = aa_perm(active, file->f_dentry, file->f_vfsmnt, mask); ++ ++ put_aaprofile(active); ++ ++out: ++ return error; ++} ++ ++static int apparmor_task_alloc_security(struct task_struct *p) ++{ ++ return aa_fork(p); ++} ++ ++static void apparmor_task_free_security(struct task_struct *p) ++{ ++ aa_release(p); ++} ++ ++static int apparmor_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, ++ int flags) ++{ ++ return cap_task_post_setuid(id0, id1, id2, flags); ++} ++ ++static void apparmor_task_reparent_to_init(struct task_struct *p) ++{ ++ cap_task_reparent_to_init(p); ++ return; ++} ++ ++static int apparmor_getprocattr(struct task_struct *p, char *name, void *value, ++ size_t size) ++{ ++ int error; ++ struct aaprofile *active; ++ char *str = value; ++ ++ /* Subdomain only supports the "current" process attribute */ ++ if (strcmp(name, "current") != 0) { ++ error = -EINVAL; ++ goto out; ++ } ++ ++ if (!size) { ++ error = -ERANGE; ++ goto out; ++ } ++ ++ /* must be task querying itself or admin */ ++ if (current != p && !capable(CAP_SYS_ADMIN)) { ++ error = -EPERM; ++ goto out; ++ } ++ ++ active = get_task_active_aaprofile(p); ++ error = aa_getprocattr(active, str, size); ++ put_aaprofile(active); ++ ++out: ++ return error; ++} ++ ++static int apparmor_setprocattr(struct task_struct *p, char *name, void *value, ++ size_t size) ++{ ++ const char *cmd_changehat = "changehat ", ++ *cmd_setprofile = "setprofile "; ++ ++ int error = -EACCES; /* default to a perm denied */ ++ char *cmd = (char *)value; ++ ++ /* only support messages to current */ ++ if (strcmp(name, "current") != 0) { ++ error = -EINVAL; ++ goto out; ++ } ++ ++ if (!size) { ++ error = -ERANGE; ++ goto out; ++ } ++ ++ /* CHANGE HAT -- switch task into a subhat (subprofile) if defined */ ++ if (size > strlen(cmd_changehat) && ++ strncmp(cmd, cmd_changehat, strlen(cmd_changehat)) == 0) { ++ char *hatinfo = cmd + strlen(cmd_changehat); ++ size_t infosize = size - strlen(cmd_changehat); ++ ++ /* Only the current process may change it's hat */ ++ if (current != p) { ++ AA_WARN("%s: Attempt by foreign task %s(%d) " ++ "[user %d] to changehat of task %s(%d)\n", ++ __FUNCTION__, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ ++ error = -EACCES; ++ goto out; ++ } ++ ++ error = aa_setprocattr_changehat(hatinfo, infosize); ++ if (error == 0) ++ /* success, set return to #bytes in orig request */ ++ error = size; ++ ++ /* SET NEW PROFILE */ ++ } else if (size > strlen(cmd_setprofile) && ++ strncmp(cmd, cmd_setprofile, strlen(cmd_setprofile)) == 0) { ++ struct aaprofile *active; ++ ++ /* only an unconfined process with admin capabilities ++ * may change the profile of another task ++ */ ++ ++ if (!capable(CAP_SYS_ADMIN)) { ++ AA_WARN("%s: Unprivileged attempt by task %s(%d) " ++ "[user %d] to assign profile to task %s(%d)\n", ++ __FUNCTION__, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ error = -EACCES; ++ goto out; ++ } ++ ++ active = get_active_aaprofile(); ++ if (!active) { ++ char *profile = cmd + strlen(cmd_setprofile); ++ size_t profilesize = size - strlen(cmd_setprofile); ++ ++ error = aa_setprocattr_setprofile(p, profile, profilesize); ++ if (error == 0) ++ /* success, ++ * set return to #bytes in orig request ++ */ ++ error = size; ++ } else { ++ AA_WARN("%s: Attempt by confined task %s(%d) " ++ "[user %d] to assign profile to task %s(%d)\n", ++ __FUNCTION__, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ ++ error = -EACCES; ++ } ++ put_aaprofile(active); ++ } else { ++ /* unknown operation */ ++ AA_WARN("%s: Unknown setprocattr command '%.*s' by task %s(%d) " ++ "[user %d] for task %s(%d)\n", ++ __FUNCTION__, ++ size < 16 ? (int)size : 16, ++ cmd, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ ++ error = -EINVAL; ++ } ++ ++out: ++ return error; ++} ++ ++struct security_operations apparmor_ops = { ++ .ptrace = apparmor_ptrace, ++ .capget = apparmor_capget, ++ .capset_check = apparmor_capset_check, ++ .capset_set = apparmor_capset_set, ++ .sysctl = apparmor_sysctl, ++ .capable = apparmor_capable, ++ .syslog = apparmor_syslog, ++ ++ .netlink_send = apparmor_netlink_send, ++ .netlink_recv = apparmor_netlink_recv, ++ ++ .bprm_apply_creds = apparmor_bprm_apply_creds, ++ .bprm_set_security = apparmor_bprm_set_security, ++ ++ .sb_mount = apparmor_sb_mount, ++ .sb_umount = apparmor_umount, ++ ++ .inode_mkdir = apparmor_inode_mkdir, ++ .inode_rmdir = apparmor_inode_rmdir, ++ .inode_create = apparmor_inode_create, ++ .inode_link = apparmor_inode_link, ++ .inode_unlink = apparmor_inode_unlink, ++ .inode_mknod = apparmor_inode_mknod, ++ .inode_rename = apparmor_inode_rename, ++ .inode_permission = apparmor_inode_permission, ++ .inode_setattr = apparmor_inode_setattr, ++ .inode_setxattr = apparmor_inode_setxattr, ++ .inode_getxattr = apparmor_inode_getxattr, ++ .inode_listxattr = apparmor_inode_listxattr, ++ .inode_removexattr = apparmor_inode_removexattr, ++ .file_permission = apparmor_file_permission, ++ .file_alloc_security = apparmor_file_alloc_security, ++ .file_free_security = apparmor_file_free_security, ++ .file_mmap = apparmor_file_mmap, ++ ++ .task_alloc_security = apparmor_task_alloc_security, ++ .task_free_security = apparmor_task_free_security, ++ .task_post_setuid = apparmor_task_post_setuid, ++ .task_reparent_to_init = apparmor_task_reparent_to_init, ++ ++ .getprocattr = apparmor_getprocattr, ++ .setprocattr = apparmor_setprocattr, ++}; ++ ++static int __init apparmor_init(void) ++{ ++ int error; ++ const char *complainmsg = ": complainmode enabled"; ++ ++ if ((error = create_apparmorfs())) { ++ AA_ERROR("Unable to activate AppArmor filesystem\n"); ++ goto createfs_out; ++ } ++ ++ if ((error = alloc_null_complain_profile())){ ++ AA_ERROR("Unable to allocate null complain profile\n"); ++ goto alloc_out; ++ } ++ ++ if ((error = register_security(&apparmor_ops))) { ++ AA_ERROR("Unable to load AppArmor\n"); ++ goto register_security_out; ++ } ++ ++ AA_INFO("AppArmor initialized%s\n", ++ apparmor_complain ? complainmsg : ""); ++ aa_audit_message(NULL, GFP_KERNEL, 0, ++ "AppArmor initialized%s\n", ++ apparmor_complain ? complainmsg : ""); ++ ++ return error; ++ ++register_security_out: ++ free_null_complain_profile(); ++ ++alloc_out: ++ (void)destroy_apparmorfs(); ++ ++createfs_out: ++ return error; ++ ++} ++ ++static int apparmor_exit_removeall_iter(struct subdomain *sd, void *cookie) ++{ ++ /* spin_lock(&sd_lock) held here */ ++ ++ if (__aa_is_confined(sd)) { ++ AA_DEBUG("%s: Dropping profiles %s(%d) " ++ "profile %s(%p) active %s(%p)\n", ++ __FUNCTION__, ++ sd->task->comm, sd->task->pid, ++ BASE_PROFILE(sd->active)->name, ++ BASE_PROFILE(sd->active), ++ sd->active->name, sd->active); ++ aa_switch_unconfined(sd); ++ } ++ ++ return 0; ++} ++ ++static void __exit apparmor_exit(void) ++{ ++ unsigned long flags; ++ ++ /* Remove profiles from the global profile list. ++ * This is just for tidyness as there is no way to reference this ++ * list once the AppArmor lsm hooks are detached (below) ++ */ ++ aa_profilelist_release(); ++ ++ /* Remove profiles from active tasks ++ * If this is not done, if module is reloaded after being removed, ++ * old profiles (still refcounted in memory) will become 'magically' ++ * reattached ++ */ ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ aa_subdomainlist_iterate(apparmor_exit_removeall_iter, NULL); ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++ /* Free up list of active subdomain */ ++ aa_subdomainlist_release(); ++ ++ free_null_complain_profile(); ++ ++ destroy_apparmorfs(); ++ ++ if (unregister_security(&apparmor_ops)) ++ AA_WARN("Unable to properly unregister AppArmor\n"); ++ ++ /* delay for an rcu cycle to make ensure that profiles pending ++ * destruction in the rcu callback are freed. ++ */ ++ synchronize_rcu(); ++ ++ AA_INFO("AppArmor protection removed\n"); ++ aa_audit_message(NULL, GFP_KERNEL, 0, ++ "AppArmor protection removed\n"); ++} ++ ++security_initcall(apparmor_init); ++module_exit(apparmor_exit); ++ ++MODULE_DESCRIPTION("AppArmor process confinement"); ++MODULE_AUTHOR("Tony Jones "); ++MODULE_LICENSE("GPL"); +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5003_apparmor-core-access-controls.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5003_apparmor-core-access-controls.patch new file mode 100644 index 0000000..66b44d8 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5003_apparmor-core-access-controls.patch @@ -0,0 +1,1643 @@ +This patch implements core AppArmor access control (where appropriate using +functionality provided by the sub matching module [apparmor_match]), code to +assign policy (subdomains and by connection profiles) to tasks during task +creation, remove them during task release and for determining appropiate +confinement upon domain changes (exec). It is also responsible for the low +level implementation of change_hat, switching confined tasks between their +primary and child profiles. Finally it implements the interface to the kernel +audit subsystem through which enforcement and learning events are passed to +userspace. + + +Signed-off-by: Tony Jones + +--- + security/apparmor/main.c | 1618 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 1618 insertions(+) + +--- security/apparmor/main.c.orig ++++ security/apparmor/main.c +@@ -0,0 +1,1618 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor Core ++ */ ++ ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "match/match.h" ++ ++#include "inline.h" ++ ++/* NULL complain profile ++ * ++ * Used when in complain mode, to emit Permitting messages for non-existant ++ * profiles and hats. This is necessary because of selective mode, in which ++ * case we need a complain null_profile and enforce null_profile ++ * ++ * The null_complain_profile cannot be statically allocated, because it ++ * can be associated to files which keep their reference even if subdomain is ++ * unloaded ++ */ ++struct aaprofile *null_complain_profile; ++ ++/*************************** ++ * Private utility functions ++ **************************/ ++ ++/** ++ * aa_taskattr_access ++ * @procrelname: name of file to check permission ++ * ++ * Determine if request is for write access to /proc/self/attr/current ++ * This file is the usermode iterface for changing it's hat. ++ */ ++static inline int aa_taskattr_access(const char *procrelname) ++{ ++ char buf[sizeof("/attr/current") + 10]; ++ const int maxbuflen = sizeof(buf); ++ /* assumption, 32bit pid (10 decimal digits incl \0) */ ++ ++ snprintf(buf, maxbuflen, "%d/attr/current", current->pid); ++ buf[maxbuflen - 1] = 0; ++ ++ return strcmp(buf, procrelname) == 0; ++} ++ ++/** ++ * aa_file_mode - get full mode for file entry from profile ++ * @profile: profile ++ * @name: filename ++ */ ++static inline int aa_file_mode(struct aaprofile *profile, const char *name) ++{ ++ struct aa_entry *entry; ++ int mode = 0; ++ ++ AA_DEBUG("%s: %s\n", __FUNCTION__, name); ++ if (!name) { ++ AA_DEBUG("%s: no name\n", __FUNCTION__); ++ goto out; ++ } ++ ++ if (!profile) { ++ AA_DEBUG("%s: no profile\n", __FUNCTION__); ++ goto out; ++ } ++ list_for_each_entry(entry, &profile->file_entry, list) { ++ if (aamatch_match(name, entry->filename, ++ entry->type, entry->extradata)) ++ mode |= entry->mode; ++ } ++out: ++ return mode; ++} ++ ++/** ++ * aa_get_execmode - calculate what qualifier to apply to an exec ++ * @active: profile to search ++ * @name: name of file to exec ++ * @xmod: pointer to a execution mode bit for the rule that was matched ++ * if the rule has no execuition qualifier {pui} then ++ * %AA_MAY_EXEC is returned indicating a naked x ++ * if the has an exec qualifier then only the qualifier bit {pui} ++ * is returned (%AA_MAY_EXEC) is not set. ++ * ++ * Returns %0 (false): ++ * if unable to find profile or there are conflicting pattern matches. ++ * *xmod - is not modified ++ * ++ * Returns %1 (true): ++ * if not confined ++ * *xmod = %AA_MAY_EXEC ++ * if exec rule matched ++ * if the rule has an execution mode qualifier {pui} then ++ * *xmod = the execution qualifier of the rule {pui} ++ * else ++ * *xmod = %AA_MAY_EXEC ++ */ ++static inline int aa_get_execmode(struct aaprofile *active, const char *name, ++ int *xmod) ++{ ++ struct aa_entry *entry; ++ struct aa_entry *match = NULL; ++ ++ int pattern_match_invalid = 0, rc = 0; ++ ++ /* search list of profiles with 'x' permission ++ * this will also include entries with 'p', 'u' and 'i' ++ * qualifiers. ++ * ++ * If we find a pattern match we will keep looking for an exact match ++ * If we find conflicting pattern matches we will flag (while still ++ * looking for an exact match). If all we have is a conflict, FALSE ++ * is returned. ++ */ ++ ++ list_for_each_entry(entry, &active->file_entryp[POS_AA_MAY_EXEC], ++ listp[POS_AA_MAY_EXEC]) { ++ if (!pattern_match_invalid && ++ entry->type == aa_entry_pattern && ++ aamatch_match(name, entry->filename, ++ entry->type, entry->extradata)) { ++ if (match && ++ AA_EXEC_MASK(entry->mode) != ++ AA_EXEC_MASK(match->mode)) ++ pattern_match_invalid = 1; ++ else ++ /* keep searching for an exact match */ ++ match = entry; ++ } else if ((entry->type == aa_entry_literal || ++ (!pattern_match_invalid && ++ entry->type == aa_entry_tailglob)) && ++ aamatch_match(name, entry->filename, ++ entry->type, ++ entry->extradata)) { ++ if (entry->type == aa_entry_literal) { ++ /* got an exact match -- there can be only ++ * one, asserted at profile load time ++ */ ++ match = entry; ++ pattern_match_invalid = 0; ++ break; ++ } else { ++ if (match && ++ AA_EXEC_MASK(entry->mode) != ++ AA_EXEC_MASK(match->mode)) ++ pattern_match_invalid = 1; ++ else ++ /* got a tailglob match, keep searching ++ * for an exact match ++ */ ++ match = entry; ++ } ++ } ++ ++ } ++ ++ rc = match && !pattern_match_invalid; ++ ++ if (rc) { ++ int mode = AA_EXEC_MASK(match->mode); ++ ++ /* check for qualifiers, if present ++ * we just return the qualifier ++ */ ++ if (mode & ~AA_MAY_EXEC) ++ mode = mode & ~AA_MAY_EXEC; ++ ++ *xmod = mode; ++ } else if (!match) { ++ AA_DEBUG("%s: Unable to find execute entry in profile " ++ "for image '%s'\n", ++ __FUNCTION__, ++ name); ++ } else if (pattern_match_invalid) { ++ AA_WARN("%s: Inconsistency in profile %s. " ++ "Two (or more) patterns specify conflicting exec " ++ "qualifiers ('u', 'i' or 'p') for image %s\n", ++ __FUNCTION__, ++ active->name, ++ name); ++ } ++ ++ return rc; ++ ++ *xmod = AA_MAY_EXEC; ++ return 1; ++} ++ ++/** ++ * aa_filter_mask ++ * @mask: requested mask ++ * @inode: potential directory inode ++ * ++ * This fn performs pre-verification of the requested mask ++ * We ignore append. Previously we required 'w' on a dir to add a file. ++ * No longer. Now we require 'w' on just the file itself. Traversal 'x' is ++ * also ignored for directories. ++ * ++ * Returned value of %0 indicates no need to perform a perm check. ++ */ ++static inline int aa_filter_mask(int mask, struct inode *inode) ++{ ++ if (mask) { ++ int elim = MAY_APPEND; ++ ++ if (inode && S_ISDIR(inode->i_mode)) ++ elim |= (MAY_EXEC | MAY_WRITE); ++ ++ mask &= ~elim; ++ } ++ ++ return mask; ++} ++ ++static inline void aa_permerror2result(int perm_result, struct aa_audit *sa) ++{ ++ if (perm_result == 0) { /* success */ ++ sa->result = 1; ++ sa->error_code = 0; ++ } else { /* -ve internal error code or +ve mask of denied perms */ ++ sa->result = 0; ++ sa->error_code = perm_result; ++ } ++} ++ ++/************************* ++ * Main internal functions ++ ************************/ ++ ++/** ++ * aa_file_perm - calculate access mode for file ++ * @active: profile to check against ++ * @name: name of file to calculate mode for ++ * @mask: permission mask requested for file ++ * ++ * Search the aa_entry list in @active. ++ * Search looking to verify all permissions passed in mask. ++ * Perform the search by looking at the partitioned list of entries, one ++ * partition per permission bit. ++ * ++ * Return %0 on success, else mask of non-allowed permissions ++ */ ++static unsigned int aa_file_perm(struct aaprofile *active, const char *name, ++ int mask) ++{ ++ int i, error = 0, mode; ++ ++#define PROCPFX "/proc/" ++#define PROCLEN sizeof(PROCPFX) - 1 ++ ++ AA_DEBUG("%s: %s 0x%x\n", __FUNCTION__, name, mask); ++ ++ /* should not enter with other than R/W/X/L */ ++ WARN_ON(mask & ++ ~(AA_MAY_READ | AA_MAY_WRITE | AA_MAY_EXEC | AA_MAY_LINK)); ++ ++ /* Special case access to /proc/self/attr/current ++ * Currently we only allow access if opened O_WRONLY ++ */ ++ if (mask == MAY_WRITE && strncmp(PROCPFX, name, PROCLEN) == 0 && ++ (!list_empty(&BASE_PROFILE(active)->sub) || ++ PROFILE_COMPLAIN(active)) && aa_taskattr_access(name + PROCLEN)) ++ goto done; ++ ++ mode = 0; ++ ++ /* iterate over partition, one permission bit at a time */ ++ for (i = 0; i <= POS_AA_FILE_MAX; i++) { ++ struct aa_entry *entry; ++ ++ /* do we have to accumulate this bit? ++ * or have we already accumulated it (shortcut below)? */ ++ if (!(mask & (1 << i)) || mode & (1 << i)) ++ continue; ++ ++ list_for_each_entry(entry, &active->file_entryp[i], ++ listp[i]) { ++ if (aamatch_match(name, entry->filename, ++ entry->type, entry->extradata)) { ++ /* Shortcut, accumulate all bits present */ ++ mode |= entry->mode; ++ ++ /* Mask bits are overloaded ++ * MAY_{EXEC,WRITE,READ,APPEND} are used by ++ * kernel, other values are used locally only. ++ */ ++ if ((mode & mask) == mask) { ++ AA_DEBUG("MATCH! %s=0x%x [total mode=0x%x]\n", ++ name, mask, mode); ++ ++ goto done; ++ } ++ } ++ } ++ } ++ ++ /* return permissions not satisfied */ ++ error = mask & ~mode; ++ ++done: ++ return error; ++} ++ ++/** ++ * aa_link_perm - test permission to link to a file ++ * @active: profile to check against ++ * @link: name of link being created ++ * @target: name of target to be linked to ++ * ++ * Look up permission mode on both @link and @target. @link must have same ++ * permission mode as @target. At least @link must have the link bit enabled. ++ * Return %0 on success, error otherwise. ++ */ ++static int aa_link_perm(struct aaprofile *active, ++ const char *link, const char *target) ++{ ++ int l_mode, t_mode, ret; ++ ++ l_mode = aa_file_mode(active, link); ++ if (l_mode & AA_MAY_LINK) { ++ /* mask off link bit */ ++ l_mode &= ~AA_MAY_LINK; ++ ++ t_mode = aa_file_mode(active, target); ++ t_mode &= ~AA_MAY_LINK; ++ ++ ret = (l_mode == t_mode); ++ } else { ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/** ++ * _aa_perm_dentry ++ * @active: profile to check against ++ * @dentry: requested dentry ++ * @mask: mask of requested operations ++ * @pname: pointer to hold matched pathname (if any) ++ * ++ * Helper function. Obtain pathname for specified dentry. Verify if profile ++ * authorizes mask operations on pathname (due to lack of vfsmnt it is sadly ++ * necessary to search mountpoints in namespace -- when nameidata is passed ++ * more fully, this code can go away). If more than one mountpoint matches ++ * but none satisfy the profile, only the first pathname (mountpoint) is ++ * returned for subsequent logging. ++ * ++ * Return %0 (success), +ve (mask of permissions not satisfied) or -ve (system ++ * error, most likely -%ENOMEM). ++ */ ++static int _aa_perm_dentry(struct aaprofile *active, struct dentry *dentry, ++ int mask, const char **pname) ++{ ++ char *name = NULL, *failed_name = NULL; ++ struct aa_path_data data; ++ int error = 0, failed_error = 0, path_error, ++ complain = PROFILE_COMPLAIN(active); ++ ++ /* search all paths to dentry */ ++ ++ aa_path_begin(dentry, &data); ++ do { ++ name = aa_path_getname(&data); ++ if (name) { ++ /* error here is 0 (success) or +ve (mask of perms) */ ++ error = aa_file_perm(active, name, mask); ++ ++ /* access via any path is enough */ ++ if (complain || error == 0) ++ break; /* Caller must free name */ ++ ++ /* Already have an path that failed? */ ++ if (failed_name) { ++ aa_put_name(name); ++ } else { ++ failed_name = name; ++ failed_error = error; ++ } ++ } ++ } while (name); ++ ++ if ((path_error = aa_path_end(&data)) != 0) { ++ AA_ERROR("%s: An error occured while translating dentry %p " ++ "inode# %lu to a pathname. Error %d\n", ++ __FUNCTION__, ++ dentry, ++ dentry->d_inode->i_ino, ++ path_error); ++ ++ WARN_ON(name); /* name should not be set if error */ ++ error = path_error; ++ name = NULL; ++ } else if (name) { ++ if (failed_name) ++ aa_put_name(failed_name); ++ } else { ++ name = failed_name; ++ error = failed_error; ++ } ++ ++ *pname = name; ++ ++ return error; ++} ++ ++/************************** ++ * Global utility functions ++ *************************/ ++ ++/** ++ * attach_nullprofile - allocate and attach a null_profile hat to profile ++ * @profile: profile to attach a null_profile hat to. ++ * ++ * Return %0 (success) or error (-%ENOMEM) ++ */ ++int attach_nullprofile(struct aaprofile *profile) ++{ ++ struct aaprofile *hat = NULL; ++ char *hatname = NULL; ++ ++ hat = alloc_aaprofile(); ++ if (!hat) ++ goto fail; ++ if (profile->flags.complain) ++ hatname = kstrdup("null-complain-profile", GFP_KERNEL); ++ else ++ hatname = kstrdup("null-profile", GFP_KERNEL); ++ if (!hatname) ++ goto fail; ++ ++ hat->flags.complain = profile->flags.complain; ++ hat->name = hatname; ++ hat->parent = profile; ++ ++ profile->null_profile = hat; ++ ++ return 0; ++ ++fail: ++ kfree(hatname); ++ free_aaprofile(hat); ++ ++ return -ENOMEM; ++} ++ ++ ++/** ++ * alloc_null_complain_profile - Allocate the global null_complain_profile. ++ * ++ * Return %0 (success) or error (-%ENOMEM) ++ */ ++int alloc_null_complain_profile(void) ++{ ++ null_complain_profile = alloc_aaprofile(); ++ if (!null_complain_profile) ++ goto fail; ++ ++ null_complain_profile->name = ++ kstrdup("null-complain-profile", GFP_KERNEL); ++ ++ if (!null_complain_profile->name) ++ goto fail; ++ ++ null_complain_profile->flags.complain = 1; ++ if (attach_nullprofile(null_complain_profile)) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ /* free_aaprofile is safe for freeing partially constructed objects */ ++ free_aaprofile(null_complain_profile); ++ null_complain_profile = NULL; ++ ++ return -ENOMEM; ++} ++ ++/** ++ * free_null_complain_profile - Free null profiles ++ */ ++void free_null_complain_profile(void) ++{ ++ put_aaprofile(null_complain_profile); ++ null_complain_profile = NULL; ++} ++ ++/** ++ * aa_audit_message - Log a message to the audit subsystem ++ * @active: profile to check against ++ * @gfp: allocation flags ++ * @flags: audit flags ++ * @fmt: varargs fmt ++ */ ++int aa_audit_message(struct aaprofile *active, unsigned int gfp, int flags, ++ const char *fmt, ...) ++{ ++ int ret; ++ struct aa_audit sa; ++ ++ sa.type = AA_AUDITTYPE_MSG; ++ sa.name = fmt; ++ va_start(sa.vaval, fmt); ++ sa.flags = flags; ++ sa.gfp_mask = gfp; ++ sa.error_code = 0; ++ sa.result = 0; /* fake failure: force message to be logged */ ++ ++ ret = aa_audit(active, &sa); ++ ++ va_end(sa.vaval); ++ ++ return ret; ++} ++ ++/** ++ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem ++ * @active: profile to check against ++ * @msg: string describing syscall being rejected ++ * @gfp: memory allocation flags ++ */ ++int aa_audit_syscallreject(struct aaprofile *active, unsigned int gfp, ++ const char *msg) ++{ ++ struct aa_audit sa; ++ ++ sa.type = AA_AUDITTYPE_SYSCALL; ++ sa.name = msg; ++ sa.flags = 0; ++ sa.gfp_mask = gfp; ++ sa.error_code = 0; ++ sa.result = 0; /* failure */ ++ ++ return aa_audit(active, &sa); ++} ++ ++/** ++ * aa_audit - Log an audit event to the audit subsystem ++ * @active: profile to check against ++ * @sa: audit event ++ */ ++int aa_audit(struct aaprofile *active, const struct aa_audit *sa) ++{ ++ struct audit_buffer *ab = NULL; ++ struct audit_context *ctx; ++ ++ const char *logcls; ++ unsigned int flags; ++ int audit = 0, ++ complain = 0, ++ error = -EINVAL, ++ opspec_error = -EACCES; ++ ++ const unsigned int gfp_mask = sa->gfp_mask; ++ ++ WARN_ON(sa->type >= AA_AUDITTYPE__END); ++ ++ /* ++ * sa->result: 1 success, 0 failure ++ * sa->error_code: success: 0 ++ * failure: +ve mask of failed permissions or -ve ++ * system error ++ */ ++ ++ if (likely(sa->result)) { ++ if (likely(!PROFILE_AUDIT(active))) { ++ /* nothing to log */ ++ error = 0; ++ goto out; ++ } else { ++ audit = 1; ++ logcls = "AUDITING"; ++ } ++ } else if (sa->error_code < 0) { ++ audit_log(current->audit_context, gfp_mask, AUDIT_AA, ++ "Internal error auditing event type %d (error %d)", ++ sa->type, sa->error_code); ++ AA_ERROR("Internal error auditing event type %d (error %d)\n", ++ sa->type, sa->error_code); ++ error = sa->error_code; ++ goto out; ++ } else if (sa->type == AA_AUDITTYPE_SYSCALL) { ++ /* Currently AA_AUDITTYPE_SYSCALL is for rejects only. ++ * Values set by aa_audit_syscallreject will get us here. ++ */ ++ logcls = "REJECTING"; ++ } else { ++ complain = PROFILE_COMPLAIN(active); ++ logcls = complain ? "PERMITTING" : "REJECTING"; ++ } ++ ++ /* In future extend w/ per-profile flags ++ * (flags |= sa->active->flags) ++ */ ++ flags = sa->flags; ++ if (apparmor_logsyscall) ++ flags |= AA_AUDITFLAG_AUDITSS_SYSCALL; ++ ++ ++ /* Force full audit syscall logging regardless of global setting if ++ * we are rejecting a syscall ++ */ ++ if (sa->type == AA_AUDITTYPE_SYSCALL) { ++ ctx = current->audit_context; ++ } else { ++ ctx = (flags & AA_AUDITFLAG_AUDITSS_SYSCALL) ? ++ current->audit_context : NULL; ++ } ++ ++ ab = audit_log_start(ctx, gfp_mask, AUDIT_AA); ++ ++ if (!ab) { ++ AA_ERROR("Unable to log event (%d) to audit subsys\n", ++ sa->type); ++ if (complain) ++ error = 0; ++ goto out; ++ } ++ ++ /* messages get special handling */ ++ if (sa->type == AA_AUDITTYPE_MSG) { ++ audit_log_vformat(ab, sa->name, sa->vaval); ++ audit_log_end(ab); ++ error = 0; ++ goto out; ++ } ++ ++ /* log operation */ ++ ++ audit_log_format(ab, "%s ", logcls); /* REJECTING/ALLOWING/etc */ ++ ++ if (sa->type == AA_AUDITTYPE_FILE) { ++ int perm = audit ? sa->ival : sa->error_code; ++ ++ audit_log_format(ab, "%s%s%s%s access to %s ", ++ perm & AA_MAY_READ ? "r" : "", ++ perm & AA_MAY_WRITE ? "w" : "", ++ perm & AA_MAY_EXEC ? "x" : "", ++ perm & AA_MAY_LINK ? "l" : "", ++ sa->name); ++ ++ opspec_error = -EPERM; ++ ++ } else if (sa->type == AA_AUDITTYPE_DIR) { ++ audit_log_format(ab, "%s on %s ", ++ sa->ival == aa_dir_mkdir ? "mkdir" : "rmdir", ++ sa->name); ++ ++ } else if (sa->type == AA_AUDITTYPE_ATTR) { ++ struct iattr *iattr = (struct iattr*)sa->pval; ++ ++ audit_log_format(ab, ++ "attribute (%s%s%s%s%s%s%s) change to %s ", ++ iattr->ia_valid & ATTR_MODE ? "mode," : "", ++ iattr->ia_valid & ATTR_UID ? "uid," : "", ++ iattr->ia_valid & ATTR_GID ? "gid," : "", ++ iattr->ia_valid & ATTR_SIZE ? "size," : "", ++ ((iattr->ia_valid & ATTR_ATIME_SET) || ++ (iattr->ia_valid & ATTR_ATIME)) ? "atime," : "", ++ ((iattr->ia_valid & ATTR_MTIME_SET) || ++ (iattr->ia_valid & ATTR_MTIME)) ? "mtime," : "", ++ iattr->ia_valid & ATTR_CTIME ? "ctime," : "", ++ sa->name); ++ ++ } else if (sa->type == AA_AUDITTYPE_XATTR) { ++ const char *fmt; ++ switch (sa->ival) { ++ case aa_xattr_get: ++ fmt = "xattr get"; ++ break; ++ case aa_xattr_set: ++ fmt = "xattr set"; ++ break; ++ case aa_xattr_list: ++ fmt = "xattr list"; ++ break; ++ case aa_xattr_remove: ++ fmt = "xattr remove"; ++ break; ++ default: ++ fmt = "xattr "; ++ break; ++ } ++ ++ audit_log_format(ab, "%s on %s ", fmt, sa->name); ++ ++ } else if (sa->type == AA_AUDITTYPE_LINK) { ++ audit_log_format(ab, ++ "link access from %s to %s ", ++ sa->name, ++ (char*)sa->pval); ++ ++ } else if (sa->type == AA_AUDITTYPE_CAP) { ++ audit_log_format(ab, ++ "access to capability '%s' ", ++ capability_to_name(sa->ival)); ++ ++ opspec_error = -EPERM; ++ } else if (sa->type == AA_AUDITTYPE_SYSCALL) { ++ audit_log_format(ab, "access to syscall '%s' ", sa->name); ++ ++ opspec_error = -EPERM; ++ } else { ++ /* -EINVAL -- will WARN_ON above */ ++ goto out; ++ } ++ ++ audit_log_format(ab, "(%s(%d) ", current->comm, current->pid); ++ ++ if (0) ++ audit_log_format(ab, "[global deny])"); ++ else ++ audit_log_format(ab, "profile %s active %s)", ++ BASE_PROFILE(active)->name, ++ active->name); ++ ++ audit_log_end(ab); ++ ++ if (complain) ++ error = 0; ++ else ++ error = sa->result ? 0 : opspec_error; ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_get_name - retrieve fully qualified path name ++ * @dentry: relative path element ++ * @mnt: where in tree ++ * ++ * Returns fully qualified path name on sucess, NULL on failure. ++ * aa_put_name must be used to free allocated buffer. ++ */ ++char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt) ++{ ++ char *page, *name = NULL; ++ ++ page = (char *)__get_free_page(GFP_KERNEL); ++ if (!page) ++ goto out; ++ ++ name = d_path_flags(dentry, mnt, page, PAGE_SIZE, ++ DPATH_SYSROOT|DPATH_NODELETED); ++ ++ AA_DEBUG("%s: full_path=%s\n", __FUNCTION__, name); ++out: ++ return name; ++} ++ ++/*********************************** ++ * Global permission check functions ++ ***********************************/ ++ ++/** ++ * aa_attr - check whether attribute change allowed ++ * @active: profile to check against ++ * @dentry: file to check ++ * @iattr: attribute changes requested ++ */ ++int aa_attr(struct aaprofile *active, struct dentry *dentry, ++ struct iattr *iattr) ++{ ++ int error = 0, permerror; ++ struct aa_audit sa; ++ ++ sa.type = AA_AUDITTYPE_ATTR; ++ sa.pval = iattr; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _aa_perm_dentry(active, dentry, MAY_WRITE, &sa.name); ++ aa_permerror2result(permerror, &sa); ++ ++ error = aa_audit(active, &sa); ++ ++ aa_put_name(sa.name); ++ ++ return error; ++} ++ ++/** ++ * aa_xattr - check whether xattr attribute change allowed ++ * @active: profile to check against ++ * @dentry: file to check ++ * @xattr: xattr to check ++ * @xattroptype: type of xattr operation ++ */ ++int aa_xattr(struct aaprofile *active, struct dentry *dentry, ++ const char *xattr, enum aa_xattroptype xattroptype) ++{ ++ int error = 0, permerror, mask = 0; ++ struct aa_audit sa; ++ ++ /* if not confined or empty mask permission granted */ ++ if (!active) ++ goto out; ++ ++ if (xattroptype == aa_xattr_get || xattroptype == aa_xattr_list) ++ mask = MAY_READ; ++ else if (xattroptype == aa_xattr_set || xattroptype == aa_xattr_remove) ++ mask = MAY_WRITE; ++ ++ sa.type = AA_AUDITTYPE_XATTR; ++ sa.ival = xattroptype; ++ sa.pval = xattr; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _aa_perm_dentry(active, dentry, mask, &sa.name); ++ aa_permerror2result(permerror, &sa); ++ ++ error = aa_audit(active, &sa); ++ ++ aa_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_perm - basic subdomain permissions check ++ * @active: profile to check against ++ * @dentry: dentry ++ * @mnt: mountpoint ++ * @mask: access mode requested ++ * ++ * Determine if access (mask) for dentry is authorized by subdomain active ++ * profile. Result, %0 (success), -ve (error) ++ */ ++int aa_perm(struct aaprofile *active, struct dentry *dentry, ++ struct vfsmount *mnt, int mask) ++{ ++ int error = 0, permerror; ++ struct aa_audit sa; ++ ++ if (!active) ++ goto out; ++ ++ if ((mask = aa_filter_mask(mask, dentry->d_inode)) == 0) ++ goto out; ++ ++ sa.type = AA_AUDITTYPE_FILE; ++ sa.name = aa_get_name(dentry, mnt); ++ sa.ival = mask; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = (sa.name ? aa_file_perm(active, sa.name, mask) : -ENOMEM); ++ ++ aa_permerror2result(permerror, &sa); ++ ++ error = aa_audit(active, &sa); ++ ++ aa_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_perm_nameidata: interface to sd_perm accepting nameidata ++ * @active: profile to check against ++ * @nd: namespace data (for vfsmnt and dentry) ++ * @mask: access mode requested ++ */ ++int aa_perm_nameidata(struct aaprofile *active, struct nameidata *nd, int mask) ++{ ++ int error = 0; ++ ++ if (nd) ++ error = aa_perm(active, nd->dentry, nd->mnt, mask); ++ ++ return error; ++} ++ ++/** ++ * aa_perm_dentry - file permissions interface when no vfsmnt available ++ * @active: profile to check against ++ * @dentry: requested dentry ++ * @mask: access mode requested ++ * ++ * Determine if access (mask) for dentry is authorized by active profile. ++ * Result, %0 (success), -ve (error) ++ */ ++int aa_perm_dentry(struct aaprofile *active, struct dentry *dentry, int mask) ++{ ++ int error = 0, permerror; ++ struct aa_audit sa; ++ ++ if (!active) ++ goto out; ++ ++ if ((mask = aa_filter_mask(mask, dentry->d_inode)) == 0) ++ goto out; ++ ++ sa.type = AA_AUDITTYPE_FILE; ++ sa.ival = mask; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _aa_perm_dentry(active, dentry, mask, &sa.name); ++ aa_permerror2result(permerror, &sa); ++ ++ error = aa_audit(active, &sa); ++ ++ aa_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_perm_dir ++ * @active: profile to check against ++ * @dentry: requested dentry ++ * @diroptype: aa_dir_mkdir or aa_dir_rmdir ++ * ++ * Determine if directory operation (make/remove) for dentry is authorized ++ * by @active profile. ++ * Result, %0 (success), -ve (error) ++ */ ++int aa_perm_dir(struct aaprofile *active, struct dentry *dentry, ++ enum aa_diroptype diroptype) ++{ ++ int error = 0, permerror, mask; ++ struct aa_audit sa; ++ ++ WARN_ON(diroptype != aa_dir_mkdir && diroptype != aa_dir_rmdir); ++ ++ if (!active) ++ goto out; ++ ++ mask = MAY_WRITE; ++ ++ sa.type = AA_AUDITTYPE_DIR; ++ sa.ival = diroptype; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _aa_perm_dentry(active, dentry, mask, &sa.name); ++ aa_permerror2result(permerror, &sa); ++ ++ error = aa_audit(active, &sa); ++ ++ aa_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_capability - test permission to use capability ++ * @active: profile to check against ++ * @cap: capability to be tested ++ * ++ * Look up capability in active profile capability set. ++ * Return %0 (success), -%EPERM (error) ++ */ ++int aa_capability(struct aaprofile *active, int cap) ++{ ++ int error = 0; ++ ++ struct aa_audit sa; ++ ++ sa.type = AA_AUDITTYPE_CAP; ++ sa.name = NULL; ++ sa.ival = cap; ++ sa.flags = 0; ++ sa.error_code = 0; ++ sa.result = cap_raised(active->capabilities, cap); ++ sa.gfp_mask = GFP_ATOMIC; ++ ++ error = aa_audit(active, &sa); ++ ++ return error; ++} ++ ++/** ++ * aa_link - hard link check ++ * @active: profile to check against ++ * @link: dentry for link being created ++ * @target: dentry for link target ++ * ++ * Checks link permissions for all possible name combinations. This is ++ * particularly ugly. Returns %0 on sucess, error otherwise. ++ */ ++int aa_link(struct aaprofile *active, struct dentry *link, ++ struct dentry *target) ++{ ++ char *iname = NULL, *oname = NULL, ++ *failed_iname = NULL, *failed_oname = NULL; ++ unsigned int result = 0; ++ int error, path_error, error_code = 0, match = 0, ++ complain = PROFILE_COMPLAIN(active); ++ struct aa_path_data idata, odata; ++ struct aa_audit sa; ++ ++ if (!active) ++ return 0; ++ ++ /* Perform nested lookup for names. ++ * This is necessary in the case where /dev/block is mounted ++ * multiple times, i.e /dev/block->/a and /dev/block->/b ++ * This allows us to detect links where src/dest are on different ++ * mounts. N.B no support yet for links across bind mounts of ++ * the form mount -bind /mnt/subpath /mnt2 ++ * ++ * Getting direct access to vfsmounts (via nameidata) for link and ++ * target would allow all this uglyness to go away. ++ * ++ * If more than one mountpoint matches but none satisfy the profile, ++ * only the first pathname (mountpoint) is logged. ++ */ ++ ++ __aa_path_begin(target, link, &odata); ++ do { ++ oname = aa_path_getname(&odata); ++ if (oname) { ++ aa_path_begin(target, &idata); ++ do { ++ iname = aa_path_getname(&idata); ++ if (iname) { ++ result = aa_link_perm(active, oname, ++ iname); ++ ++ /* access via any path is enough */ ++ if (result || complain) { ++ match = 1; ++ break; ++ } ++ ++ /* Already have an path that failed? */ ++ if (failed_iname) { ++ aa_put_name(iname); ++ } else { ++ failed_iname = iname; ++ failed_oname = oname; ++ } ++ } ++ } while (iname && !match); ++ ++ /* should not be possible if we matched */ ++ if ((path_error = aa_path_end(&idata)) != 0) { ++ AA_ERROR("%s: An error occured while " ++ "translating inner dentry %p " ++ "inode %lu to a pathname. Error %d\n", ++ __FUNCTION__, ++ target, ++ target->d_inode->i_ino, ++ path_error); ++ ++ /* name should not be set if error */ ++ WARN_ON(iname); ++ ++ error_code = path_error; ++ } ++ ++ /* don't release if we're saving it */ ++ if (!match && failed_oname != oname) ++ aa_put_name(oname); ++ } ++ } while (oname && !match); ++ ++ if (error_code != 0) { ++ /* inner error */ ++ (void)aa_path_end(&odata); ++ } else if ((path_error = aa_path_end(&odata)) != 0) { ++ AA_ERROR("%s: An error occured while translating outer " ++ "dentry %p inode %lu to a pathname. Error %d\n", ++ __FUNCTION__, ++ link, ++ link->d_inode->i_ino, ++ path_error); ++ ++ error_code = path_error; ++ } ++ ++ if (error_code != 0) { ++ /* inner or outer error */ ++ result = 0; ++ } else if (match) { ++ result = 1; ++ } else { ++ /* failed to match */ ++ WARN_ON(iname); ++ WARN_ON(oname); ++ ++ result = 0; ++ iname = failed_iname; ++ oname = failed_oname; ++ } ++ ++ sa.type = AA_AUDITTYPE_LINK; ++ sa.name = oname; /* link */ ++ sa.pval = iname; /* target */ ++ sa.flags = 0; ++ sa.error_code = error_code; ++ sa.result = result; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ error = aa_audit(active, &sa); ++ ++ if (failed_oname != oname) ++ aa_put_name(failed_oname); ++ if (failed_iname != iname) ++ aa_put_name(failed_iname); ++ ++ aa_put_name(oname); ++ aa_put_name(iname); ++ ++ return error; ++} ++ ++/******************************* ++ * Global task related functions ++ *******************************/ ++ ++/** ++ * aa_fork - create a new subdomain ++ * @p: new process ++ * ++ * Create a new subdomain struct for the newly created process @p. ++ * Copy parent info to child. If parent has no subdomain, child ++ * will get one with %NULL values. Return %0 on sucess. ++ * ++ * The sd_lock is used to maintain consistency against profile ++ * replacement/removal. ++ */ ++ ++int aa_fork(struct task_struct *p) ++{ ++ struct subdomain *sd = AA_SUBDOMAIN(current->security); ++ struct subdomain *newsd = alloc_subdomain(p); ++ ++ AA_DEBUG("%s\n", __FUNCTION__); ++ ++ if (!newsd) ++ return -ENOMEM; ++ ++ if (sd) { ++ unsigned long flags; ++ ++ /* Use locking here instead of getting the reference ++ * because we need both the old reference and the ++ * new reference to be consistent. ++ */ ++ spin_lock_irqsave(&sd_lock, flags); ++ aa_switch(newsd, sd->active); ++ newsd->hat_magic = sd->hat_magic; ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++ if (SUBDOMAIN_COMPLAIN(sd) && ++ sd->active == null_complain_profile) ++ LOG_HINT(sd->active, GFP_KERNEL, HINT_FORK, ++ "pid=%d child=%d\n", ++ current->pid, p->pid); ++ } ++ p->security = newsd; ++ return 0; ++} ++ ++/** ++ * aa_register - register a new program ++ * @filp: file of program being registered ++ * ++ * Try to register a new program during execve(). This should give the ++ * new program a valid subdomain. ++ */ ++int aa_register(struct file *filp) ++{ ++ char *filename; ++ struct subdomain *sd; ++ struct aaprofile *active; ++ struct aaprofile *newprofile = NULL, unconstrained_flag; ++ int error = -ENOMEM, ++ exec_mode = 0, ++ find_profile = 0, ++ find_profile_mandatory = 0, ++ complain = 0; ++ ++ AA_DEBUG("%s\n", __FUNCTION__); ++ ++ sd = AA_SUBDOMAIN(current->security); ++ ++ if (sd) { ++ complain = SUBDOMAIN_COMPLAIN(sd); ++ } else { ++ /* task has no subdomain. This can happen when a task is ++ * created when subdomain is not loaded. Allocate and ++ * attach a subdomain to the task ++ */ ++ sd = alloc_subdomain(current); ++ if (!sd) { ++ AA_WARN("%s: Failed to allocate subdomain\n", ++ __FUNCTION__); ++ goto out; ++ } ++ ++ current->security = sd; ++ } ++ ++ filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt); ++ if (!filename) { ++ AA_WARN("%s: Failed to get filename\n", __FUNCTION__); ++ goto out; ++ } ++ ++ error = 0; ++ ++ active = get_active_aaprofile(); ++ ++ if (!active) { ++ /* Unconfined task, load profile if it exists */ ++ find_profile = 1; ++ goto find_profile; ++ } ++ ++ /* Confined task, determine what mode inherit, unconstrained or ++ * mandatory to load new profile ++ */ ++ if (aa_get_execmode(active, filename, &exec_mode)) { ++ switch (exec_mode) { ++ case AA_EXEC_INHERIT: ++ /* do nothing - setting of profile ++ * already handed in aa_fork ++ */ ++ AA_DEBUG("%s: INHERIT %s\n", ++ __FUNCTION__, ++ filename); ++ break; ++ ++ case AA_EXEC_UNCONSTRAINED: ++ AA_DEBUG("%s: UNCONSTRAINED %s\n", ++ __FUNCTION__, ++ filename); ++ ++ /* unload profile */ ++ newprofile = &unconstrained_flag; ++ break; ++ ++ case AA_EXEC_PROFILE: ++ AA_DEBUG("%s: PROFILE %s\n", ++ __FUNCTION__, ++ filename); ++ ++ find_profile = 1; ++ find_profile_mandatory = 1; ++ break; ++ ++ case AA_MAY_EXEC: ++ /* this should not happen, entries ++ * with just EXEC only should be ++ * rejected at profile load time ++ */ ++ AA_ERROR("%s: Rejecting exec(2) of image '%s'. " ++ "AA_MAY_EXEC without exec qualifier invalid " ++ "(%s(%d) profile %s active %s\n", ++ __FUNCTION__, ++ filename, ++ current->comm, current->pid, ++ BASE_PROFILE(active)->name, active->name); ++ error = -EPERM; ++ break; ++ ++ default: ++ AA_ERROR("%s: Rejecting exec(2) of image '%s'. " ++ "Unknown exec qualifier %x " ++ "(%s (pid %d) profile %s active %s)\n", ++ __FUNCTION__, ++ filename, ++ exec_mode, ++ current->comm, current->pid, ++ BASE_PROFILE(active)->name, sd->active->name); ++ error = -EPERM; ++ break; ++ } ++ ++ } else if (complain) { ++ /* There was no entry in calling profile ++ * describing mode to execute image in. ++ * Drop into null-profile ++ */ ++ newprofile = get_aaprofile(null_complain_profile); ++ } else { ++ AA_WARN("%s: Rejecting exec(2) of image '%s'. " ++ "Unable to determine exec qualifier " ++ "(%s (pid %d) profile %s active %s)\n", ++ __FUNCTION__, ++ filename, ++ current->comm, current->pid, ++ BASE_PROFILE(active)->name, active->name); ++ error = -EPERM; ++ } ++ ++ ++find_profile: ++ if (!find_profile) ++ goto apply_profile; ++ ++ /* Locate new profile */ ++ newprofile = aa_profilelist_find(filename); ++ if (newprofile) { ++ AA_DEBUG("%s: setting profile %s\n", ++ __FUNCTION__, newprofile->name); ++ } else if (find_profile_mandatory) { ++ /* Profile (mandatory) could not be found */ ++ ++ if (complain) { ++ LOG_HINT(active, GFP_KERNEL, HINT_MANDPROF, ++ "image=%s pid=%d profile=%s active=%s\n", ++ filename, ++ current->pid, ++ BASE_PROFILE(active)->name, active->name); ++ ++ newprofile = get_aaprofile(null_complain_profile); ++ } else { ++ AA_WARN("REJECTING exec(2) of image '%s'. " ++ "Profile mandatory and not found " ++ "(%s(%d) profile %s active %s)\n", ++ filename, ++ current->comm, current->pid, ++ BASE_PROFILE(active)->name, active->name); ++ error = -EPERM; ++ } ++ } else { ++ /* Profile (non-mandatory) could not be found */ ++ ++ /* Only way we can get into this code is if task ++ * is unconstrained. ++ */ ++ ++ WARN_ON(active); ++ ++ AA_DEBUG("%s: No profile found for exec image %s\n", ++ __FUNCTION__, ++ filename); ++ } /* newprofile */ ++ ++ ++apply_profile: ++ /* Apply profile if necessary */ ++ if (newprofile) { ++ unsigned long flags; ++ ++ if (newprofile == &unconstrained_flag) ++ newprofile = NULL; ++ ++ /* grab a lock - this is to guarentee consistency against ++ * other writers of subdomain (replacement/removal) ++ * ++ * Several things may have changed since the code above ++ * ++ * - If we are a confined process, active is a refcounted copy ++ * of the profile that was on the subdomain at entry. ++ * This allows us to not have to hold a lock around ++ * all this code. If profile replacement has taken place ++ * our sd->active may not equal sd->active any more. ++ * This is okay since the operation is treated as if ++ * the transition occured before replacement. ++ * ++ * - If newprofile points to an actual profile (result of ++ * aa_profilelist_find above), this profile may have been ++ * replaced. We need to fix it up. Doing this to avoid ++ * having to hold a lock around all this code. ++ */ ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ ++ /* Determine if profile we found earlier is stale. ++ * If so, reobtain it. N.B stale flag should never be ++ * set on null_complain profile. ++ */ ++ if (newprofile && unlikely(newprofile->isstale)) { ++ WARN_ON(newprofile == null_complain_profile); ++ ++ /* drop refcnt obtained from earlier get_aaprofile */ ++ put_aaprofile(newprofile); ++ ++ newprofile = aa_profilelist_find(filename); ++ ++ if (!newprofile) { ++ /* Race, profile was removed, not replaced. ++ * Redo with error checking ++ */ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ goto find_profile; ++ } ++ } ++ ++ aa_switch(sd, newprofile); ++ put_aaprofile(newprofile); ++ ++ if (complain && newprofile == null_complain_profile) ++ LOG_HINT(newprofile, GFP_ATOMIC, HINT_CHGPROF, ++ "pid=%d\n", ++ current->pid); ++ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ } ++ ++ aa_put_name(filename); ++ ++ put_aaprofile(active); ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_release - release the task's subdomain ++ * @p: task being released ++ * ++ * This is called after a task has exited and the parent has reaped it. ++ * @p->security blob is freed. ++ * ++ * This is the one case where we don't need to hold the sd_lock before ++ * removing a profile from a subdomain. Once the subdomain has been ++ * removed from the subdomain_list, we are no longer racing other writers. ++ * There may still be other readers so we must still use aa_switch ++ * to put the subdomain's reference safely. ++ */ ++void aa_release(struct task_struct *p) ++{ ++ struct subdomain *sd = AA_SUBDOMAIN(p->security); ++ if (sd) { ++ p->security = NULL; ++ ++ aa_subdomainlist_remove(sd); ++ ++ aa_switch_unconfined(sd); ++ ++ kfree(sd); ++ } ++} ++ ++/***************************** ++ * global subprofile functions ++ ****************************/ ++ ++/** ++ * do_change_hat - actually switch hats ++ * @hat_name: name of hat to swtich to ++ * @sd: current subdomain ++ * ++ * Switch to a new hat. Return %0 on success, error otherwise. ++ */ ++static inline int do_change_hat(const char *hat_name, struct subdomain *sd) ++{ ++ struct aaprofile *sub; ++ int error = 0; ++ ++ sub = __aa_find_profile(hat_name, &BASE_PROFILE(sd->active)->sub); ++ ++ if (sub) { ++ /* change hat */ ++ aa_switch(sd, sub); ++ put_aaprofile(sub); ++ } else { ++ /* There is no such subprofile change to a NULL profile. ++ * The NULL profile grants no file access. ++ * ++ * This feature is used by changehat_apache. ++ * ++ * N.B from the null-profile the task can still changehat back ++ * out to the parent profile (assuming magic != NULL) ++ */ ++ if (SUBDOMAIN_COMPLAIN(sd)) { ++ LOG_HINT(sd->active, GFP_ATOMIC, HINT_UNKNOWN_HAT, ++ "%s pid=%d " ++ "profile=%s active=%s\n", ++ hat_name, ++ current->pid, ++ BASE_PROFILE(sd->active)->name, ++ sd->active->name); ++ } else { ++ AA_DEBUG("%s: Unknown hatname '%s'. " ++ "Changing to NULL profile " ++ "(%s(%d) profile %s active %s)\n", ++ __FUNCTION__, ++ hat_name, ++ current->comm, current->pid, ++ BASE_PROFILE(sd->active)->name, ++ sd->active->name); ++ error = -EACCES; ++ } ++ aa_switch(sd, sd->active->null_profile); ++ } ++ ++ return error; ++} ++ ++/** ++ * aa_change_hat - change hat to/from subprofile ++ * @hat_name: specifies hat to change to ++ * @hat_magic: token to validate hat change ++ * ++ * Change to new @hat_name when current hat is top level profile, and store ++ * the @hat_magic in the current subdomain. If the new @hat_name is ++ * %NULL, and the @hat_magic matches that stored in the current subdomain ++ * return to original top level profile. Returns %0 on success, error ++ * otherwise. ++ */ ++int aa_change_hat(const char *hat_name, u32 hat_magic) ++{ ++ struct subdomain *sd = AA_SUBDOMAIN(current->security); ++ int error = 0; ++ ++ AA_DEBUG("%s: %p, 0x%x (pid %d)\n", ++ __FUNCTION__, ++ hat_name, hat_magic, ++ current->pid); ++ ++ /* Dump out above debugging in WARN mode if we are in AUDIT mode */ ++ if (SUBDOMAIN_AUDIT(sd)) { ++ AA_WARN("%s: %s, 0x%x (pid %d)\n", ++ __FUNCTION__, hat_name ? hat_name : "NULL", ++ hat_magic, current->pid); ++ } ++ ++ /* check to see if an unconfined process is doing a changehat. */ ++ if (!__aa_is_confined(sd)) { ++ error = -EACCES; ++ goto out; ++ } ++ ++ /* Check whether current domain is parent ++ * or one of the sibling children ++ */ ++ if (!IN_SUBPROFILE(sd->active)) { ++ /* ++ * parent ++ */ ++ if (hat_name) { ++ AA_DEBUG("%s: switching to %s, 0x%x\n", ++ __FUNCTION__, ++ hat_name, ++ hat_magic); ++ ++ /* ++ * N.B hat_magic == 0 has a special meaning ++ * this indicates that the task may never changehat ++ * back to it's parent, it will stay in this subhat ++ * (or null-profile, if the hat doesn't exist) until ++ * the task terminates ++ */ ++ sd->hat_magic = hat_magic; ++ error = do_change_hat(hat_name, sd); ++ } else { ++ /* Got here via changehat(NULL, magic) ++ * ++ * We used to simply update the magic cookie. ++ * That's an odd behaviour, so just do nothing. ++ */ ++ } ++ } else { ++ /* ++ * child -- check to make sure magic is same as what was ++ * passed when we switched into this profile, ++ * Handle special casing of NULL magic which confines task ++ * to subprofile and prohibits further changehats ++ */ ++ if (hat_magic == sd->hat_magic && sd->hat_magic) { ++ if (!hat_name) { ++ /* ++ * Got here via changehat(NULL, magic) ++ * Return from subprofile, back to parent ++ */ ++ aa_switch(sd, sd->active->parent); ++ ++ /* Reset hat_magic to zero. ++ * New value will be passed on next changehat ++ */ ++ sd->hat_magic = 0; ++ } else { ++ /* change to another (sibling) profile */ ++ error = do_change_hat(hat_name, sd); ++ } ++ } else if (sd->hat_magic) { ++ AA_ERROR("KILLING process %s(%d) " ++ "Invalid change_hat() magic# 0x%x " ++ "(hatname %s profile %s active %s)\n", ++ current->comm, current->pid, ++ hat_magic, ++ hat_name ? hat_name : "NULL", ++ BASE_PROFILE(sd->active)->name, ++ sd->active->name); ++ ++ /* terminate current process */ ++ (void)send_sig_info(SIGKILL, NULL, current); ++ } else { /* sd->hat_magic == NULL */ ++ AA_ERROR("KILLING process %s(%d) " ++ "Task was confined to current subprofile " ++ "(profile %s active %s)\n", ++ current->comm, current->pid, ++ BASE_PROFILE(sd->active)->name, ++ sd->active->name); ++ ++ /* terminate current process */ ++ (void)send_sig_info(SIGKILL, NULL, current); ++ } ++ ++ } ++ ++out: ++ return error; ++} +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5004_apparmor-filesystem.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5004_apparmor-filesystem.patch new file mode 100644 index 0000000..16b00bc --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5004_apparmor-filesystem.patch @@ -0,0 +1,472 @@ +This patch implements the AppArmor file structure underneath securityfs. +Securityfs is normally mounted as /sys/kernel/security + +The following files are created under /sys/kernel/security/apparmor + control + audit - Controls the global setting for auditing all + accesses. + complain - Controls the global setting for learning mode + (usually this is set per profile rather than + globally) + debug - Controls whether debugging is enabled. + This needs to be made more fine grained + logsyscall - Controls whether when logging to the audit + subsystem full syscall auditing is enabled. + + The values by default for all of the above are 0. + + matching - Returns the features of the installed matching submodule + profiles - Returns the profiles currently loaded and for each whether + it is in complain (learning) or enforce mode. + .load + .remove + .replace - Used by userspace tools to load, remove and replace new + profiles. + + +Signed-off-by: Tony Jones + +--- + security/apparmor/apparmorfs.c | 432 +++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 432 insertions(+) + +--- security/apparmor/apparmorfs.c.orig ++++ security/apparmor/apparmorfs.c +@@ -0,0 +1,432 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor filesystem (part of securityfs) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++#include "match/match.h" ++ ++#define SECFS_AA "apparmor" ++static struct dentry *aafs_dentry = NULL; ++ ++/* profile */ ++extern struct seq_operations apparmorfs_profiles_op; ++static int aa_prof_open(struct inode *inode, struct file *file); ++static int aa_prof_release(struct inode *inode, struct file *file); ++ ++static struct file_operations apparmorfs_profiles_fops = { ++ .open = aa_prof_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = aa_prof_release, ++}; ++ ++/* matching */ ++static ssize_t aa_matching_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos); ++ ++static struct file_operations apparmorfs_matching_fops = { ++ .read = aa_matching_read, ++}; ++ ++ ++/* interface */ ++static ssize_t aa_profile_load(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos); ++static ssize_t aa_profile_replace(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos); ++static ssize_t aa_profile_remove(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos); ++ ++static struct file_operations apparmorfs_profile_load = { ++ .write = aa_profile_load ++}; ++ ++static struct file_operations apparmorfs_profile_replace = { ++ .write = aa_profile_replace ++}; ++ ++static struct file_operations apparmorfs_profile_remove = { ++ .write = aa_profile_remove ++}; ++ ++ ++/* control */ ++static u64 aa_control_get(void *data); ++static void aa_control_set(void *data, u64 val); ++ ++DEFINE_SIMPLE_ATTRIBUTE(apparmorfs_control_fops, aa_control_get, ++ aa_control_set, "%lld\n"); ++ ++ ++ ++/* table of static entries */ ++ ++static struct root_entry { ++ const char *name; ++ int mode; ++ int access; ++ struct file_operations *fops; ++ void *data; ++ ++ /* internal fields */ ++ struct dentry *dentry; ++ int parent_index; ++} root_entries[] = { ++ /* our root, normally /sys/kernel/security/apparmor */ ++ {SECFS_AA, S_IFDIR, 0550}, /* DO NOT EDIT/MOVE */ ++ ++ /* interface for obtaining list of profiles currently loaded */ ++ {"profiles", S_IFREG, 0440, &apparmorfs_profiles_fops, ++ NULL}, ++ ++ /* interface for obtaining matching features supported */ ++ {"matching", S_IFREG, 0440, &apparmorfs_matching_fops, ++ NULL}, ++ ++ /* interface for loading/removing/replacing profiles */ ++ {".load", S_IFREG, 0640, &apparmorfs_profile_load, ++ NULL}, ++ {".replace", S_IFREG, 0640, &apparmorfs_profile_replace, ++ NULL}, ++ {".remove", S_IFREG, 0640, &apparmorfs_profile_remove, ++ NULL}, ++ ++ /* interface for setting binary config values */ ++ {"control", S_IFDIR, 0550}, ++ {"complain", S_IFREG, 0640, &apparmorfs_control_fops, ++ &apparmor_complain}, ++ {"audit", S_IFREG, 0640, &apparmorfs_control_fops, ++ &apparmor_audit}, ++ {"debug", S_IFREG, 0640, &apparmorfs_control_fops, ++ &apparmor_debug}, ++ {"logsyscall", S_IFREG, 0640, &apparmorfs_control_fops, ++ &apparmor_logsyscall}, ++ {NULL, S_IFDIR, 0}, ++ ++ /* root end */ ++ {NULL, S_IFDIR, 0} ++}; ++ ++#define AAFS_DENTRY root_entries[0].dentry ++ ++static const unsigned int num_entries = ++ sizeof(root_entries) / sizeof(struct root_entry); ++ ++ ++ ++static int aa_prof_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &apparmorfs_profiles_op); ++} ++ ++ ++static int aa_prof_release(struct inode *inode, struct file *file) ++{ ++ return seq_release(inode, file); ++} ++ ++static ssize_t aa_matching_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ const char *matching = aamatch_features(); ++ ++ return simple_read_from_buffer(buf, size, ppos, matching, ++ strlen(matching)); ++} ++ ++static char *aa_simple_write_to_buffer(const char __user *userbuf, ++ size_t alloc_size, size_t copy_size, ++ loff_t *pos, const char *msg) ++{ ++ struct aaprofile *active; ++ char *data; ++ ++ if (*pos != 0) { ++ /* only writes from pos 0, that is complete writes */ ++ data = ERR_PTR(-ESPIPE); ++ goto out; ++ } ++ ++ /* Don't allow confined processes to load/replace/remove profiles. ++ * No sane person would add rules allowing this to a profile ++ * but we enforce the restriction anyways. ++ */ ++ rcu_read_lock(); ++ active = get_activeptr_rcu(); ++ if (active) { ++ AA_WARN("REJECTING access to profile %s (%s(%d) " ++ "profile %s active %s)\n", ++ msg, current->comm, current->pid, ++ BASE_PROFILE(active)->name, active->name); ++ ++ data = ERR_PTR(-EPERM); ++ goto out; ++ } ++ rcu_read_unlock(); ++ ++ data = vmalloc(alloc_size); ++ if (data == NULL) { ++ data = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ if (copy_from_user(data, userbuf, copy_size)) { ++ vfree(data); ++ data = ERR_PTR(-EFAULT); ++ goto out; ++ } ++ ++out: ++ return data; ++} ++ ++static ssize_t aa_profile_load(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ data = aa_simple_write_to_buffer(buf, size, size, pos, "load"); ++ ++ if (!IS_ERR(data)) { ++ error = aa_file_prof_add(data, size); ++ vfree(data); ++ } else { ++ error = PTR_ERR(data); ++ } ++ ++ return error; ++} ++ ++static ssize_t aa_profile_replace(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ data = aa_simple_write_to_buffer(buf, size, size, pos, "replacement"); ++ ++ if (!IS_ERR(data)) { ++ error = aa_file_prof_repl(data, size); ++ vfree(data); ++ } else { ++ error = PTR_ERR(data); ++ } ++ ++ return error; ++} ++ ++static ssize_t aa_profile_remove(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ /* aa_file_prof_remove needs a null terminated string so 1 extra ++ * byte is allocated and null the copied data is then null terminated ++ */ ++ data = aa_simple_write_to_buffer(buf, size+1, size, pos, "removal"); ++ ++ if (!IS_ERR(data)) { ++ data[size] = 0; ++ error = aa_file_prof_remove(data, size); ++ vfree(data); ++ } else { ++ error = PTR_ERR(data); ++ } ++ ++ return error; ++} ++ ++static u64 aa_control_get(void *data) ++{ ++ return *(int *)data; ++} ++ ++static void aa_control_set(void *data, u64 val) ++{ ++ if (val > 1) ++ val = 1; ++ ++ *(int*)data = (int)val; ++} ++ ++static void clear_apparmorfs(void) ++{ ++ unsigned int i; ++ ++ for (i=0; i < num_entries;i++) { ++ unsigned int index; ++ ++ if (root_entries[i].mode == S_IFDIR) { ++ if (root_entries[i].name) ++ /* defer dir free till all sub-entries freed */ ++ continue; ++ else ++ /* cleanup parent */ ++ index = root_entries[i].parent_index; ++ } else { ++ index = i; ++ } ++ ++ if (root_entries[index].dentry) { ++ securityfs_remove(root_entries[index].dentry); ++ ++ AA_DEBUG("%s: deleted apparmorfs entry name=%s " ++ "dentry=%p\n", ++ __FUNCTION__, ++ root_entries[index].name, ++ root_entries[index].dentry); ++ ++ root_entries[index].dentry = NULL; ++ root_entries[index].parent_index = 0; ++ } ++ } ++} ++ ++static int populate_apparmorfs(struct dentry *root) ++{ ++ unsigned int i, parent_index, depth; ++ ++ for (i = 0; i < num_entries; i++) { ++ root_entries[i].dentry = NULL; ++ root_entries[i].parent_index = 0; ++ } ++ ++ /* 1. Verify entry 0 is valid [sanity check] */ ++ if (num_entries == 0 || ++ !root_entries[0].name || ++ strcmp(root_entries[0].name, SECFS_AA) != 0 || ++ root_entries[0].mode != S_IFDIR) { ++ AA_ERROR("%s: root entry 0 is not SECFS_AA/dir\n", ++ __FUNCTION__); ++ goto error; ++ } ++ ++ /* 2. Build back pointers */ ++ parent_index = 0; ++ depth = 1; ++ ++ for (i = 1; i < num_entries; i++) { ++ root_entries[i].parent_index = parent_index; ++ ++ if (root_entries[i].name && ++ root_entries[i].mode == S_IFDIR) { ++ depth++; ++ parent_index = i; ++ } else if (!root_entries[i].name) { ++ if (root_entries[i].mode != S_IFDIR || depth == 0) { ++ AA_ERROR("%s: root_entry %d invalid (%u %d)", ++ __FUNCTION__, i, ++ root_entries[i].mode, ++ root_entries[i].parent_index); ++ goto error; ++ } ++ ++ depth--; ++ parent_index = root_entries[parent_index].parent_index; ++ } ++ } ++ ++ if (depth != 0) { ++ AA_ERROR("%s: root_entry table not correctly terminated\n", ++ __FUNCTION__); ++ goto error; ++ } ++ ++ /* 3. Create root (parent=NULL) */ ++ root_entries[0].dentry = securityfs_create_file( ++ root_entries[0].name, ++ root_entries[0].mode | ++ root_entries[0].access, ++ NULL, NULL, NULL); ++ ++ if (IS_ERR(root_entries[0].dentry)) ++ goto error; ++ else ++ AA_DEBUG("%s: created securityfs/apparmor [dentry=%p]\n", ++ __FUNCTION__, root_entries[0].dentry); ++ ++ ++ /* 4. create remaining nodes */ ++ for (i = 1; i < num_entries; i++) { ++ struct dentry *parent; ++ void *data = NULL; ++ struct file_operations *fops = NULL; ++ ++ /* end of directory ? */ ++ if (!root_entries[i].name) ++ continue; ++ ++ parent = root_entries[root_entries[i].parent_index].dentry; ++ ++ if (root_entries[i].mode != S_IFDIR) { ++ data = root_entries[i].data; ++ fops = root_entries[i].fops; ++ } ++ ++ root_entries[i].dentry = securityfs_create_file( ++ root_entries[i].name, ++ root_entries[i].mode | ++ root_entries[i].access, ++ parent, ++ data, ++ fops); ++ ++ if (IS_ERR(root_entries[i].dentry)) ++ goto cleanup_error; ++ ++ AA_DEBUG("%s: added apparmorfs entry " ++ "name=%s mode=%x dentry=%p [parent %p]\n", ++ __FUNCTION__, root_entries[i].name, ++ root_entries[i].mode|root_entries[i].access, ++ root_entries[i].dentry, parent); ++ } ++ ++ return 0; ++ ++cleanup_error: ++ clear_apparmorfs(); ++ ++error: ++ return -EINVAL; ++} ++ ++int create_apparmorfs(void) ++{ ++ int error = 0; ++ ++ if (AAFS_DENTRY) { ++ error = -EEXIST; ++ AA_ERROR("%s: Subdomain securityfs already exists\n", ++ __FUNCTION__); ++ } else { ++ error = populate_apparmorfs(aafs_dentry); ++ if (error != 0) { ++ AA_ERROR("%s: Error populating Subdomain securityfs\n", ++ __FUNCTION__); ++ } ++ } ++ ++ return error; ++} ++ ++void destroy_apparmorfs(void) ++{ ++ if (AAFS_DENTRY) ++ clear_apparmorfs(); ++} +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5005_apparmor-userspace-interface.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5005_apparmor-userspace-interface.patch new file mode 100644 index 0000000..a460e65 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5005_apparmor-userspace-interface.patch @@ -0,0 +1,907 @@ +This patch implements the interface between the userspace policy loader +and the kernel module. It is called by the .load, .remove and .replace +file_operations hooks implemented in apparmorfs.c. + +The code is reponsible for serializing data in a platform independant +manner from userspace and creating/activating the necessary apparmor +profiles. + +Certain aspects are delegated to the sub matching module which implements +the aamatch_* functions. + + +Signed-off-by: Tony Jones + +--- + security/apparmor/module_interface.c | 840 +++++++++++++++++++++++++++++++++++ + security/apparmor/module_interface.h | 37 + + 2 files changed, 877 insertions(+) + +--- security/apparmor/module_interface.c.orig ++++ security/apparmor/module_interface.c +@@ -0,0 +1,840 @@ ++/* ++ * Copyright (C) 1998-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor userspace policy interface ++ */ ++ ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++#include "module_interface.h" ++#include "match/match.h" ++ ++/* aa_code defined in module_interface.h */ ++ ++const int aacode_datasize[] = { 1, 2, 4, 8, 2, 2, 4, 0, 0, 0, 0, 0, 0 }; ++ ++struct aa_taskreplace_data { ++ struct aaprofile *old_profile; ++ struct aaprofile *new_profile; ++}; ++ ++/* inlines must be forward of there use in newer version of gcc, ++ just forward declaring with a prototype won't work anymore */ ++ ++static inline void free_aa_entry(struct aa_entry *entry) ++{ ++ if (entry) { ++ kfree(entry->filename); ++ aamatch_free(entry->extradata); ++ kfree(entry); ++ } ++} ++ ++/** ++ * alloc_aa_entry - create new empty aa_entry ++ * This routine allocates, initializes, and returns a new aa_entry ++ * file entry structure. Structure is zeroed. Returns new structure on ++ * success, %NULL on failure. ++ */ ++static inline struct aa_entry *alloc_aa_entry(void) ++{ ++ struct aa_entry *entry; ++ ++ AA_DEBUG("%s\n", __FUNCTION__); ++ entry = kzalloc(sizeof(struct aa_entry), GFP_KERNEL); ++ if (entry) { ++ int i; ++ INIT_LIST_HEAD(&entry->list); ++ for (i = 0; i <= POS_AA_FILE_MAX; i++) { ++ INIT_LIST_HEAD(&entry->listp[i]); ++ } ++ } ++ return entry; ++} ++ ++/** ++ * free_aaprofile_rcu - rcu callback for free profiles ++ * @head: rcu_head struct of the profile whose reference is being put. ++ * ++ * the rcu callback routine, which delays the freeing of a profile when ++ * its last reference is put. ++ */ ++static void free_aaprofile_rcu(struct rcu_head *head) ++{ ++ struct aaprofile *p = container_of(head, struct aaprofile, rcu); ++ free_aaprofile(p); ++} ++ ++/** ++ * task_remove - remove profile from a task's subdomain ++ * @sd: task's subdomain ++ * ++ * remove the active profile from a task's subdomain, switching the task ++ * to an unconfined state. ++ */ ++static inline void task_remove(struct subdomain *sd) ++{ ++ /* spin_lock(&sd_lock) held here */ ++ AA_DEBUG("%s: removing profile from task %s(%d) profile %s active %s\n", ++ __FUNCTION__, ++ sd->task->comm, ++ sd->task->pid, ++ BASE_PROFILE(sd->active)->name, ++ sd->active->name); ++ ++ aa_switch_unconfined(sd); ++} ++ ++/** taskremove_iter - Iterator to unconfine subdomains which match cookie ++ * @sd: subdomain to consider for profile removal ++ * @cookie: pointer to the oldprofile which is being removed ++ * ++ * If the subdomain's active profile matches old_profile, then call ++ * task_remove() to remove the profile leaving the task (subdomain) unconfined. ++ */ ++static int taskremove_iter(struct subdomain *sd, void *cookie) ++{ ++ struct aaprofile *old_profile = (struct aaprofile *)cookie; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ ++ if (__aa_is_confined(sd) && BASE_PROFILE(sd->active) == old_profile) { ++ task_remove(sd); ++ } ++ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++ return 0; ++} ++ ++/** task_replace - replace subdomain's current profile with a new profile ++ * @sd: subdomain to replace the profile on ++ * @new: new profile ++ * ++ * Replace a task's (subdomain's) active profile with a new profile. If ++ * task was in a hat then the new profile will also be in the equivalent ++ * hat in the new profile if it exists. If it doesn't exist the ++ * task will be placed in the special null_profile state. ++ */ ++static inline void task_replace(struct subdomain *sd, struct aaprofile *new) ++{ ++ AA_DEBUG("%s: replacing profile for task %s(%d) " ++ "profile=%s (%p) active=%s (%p)\n", ++ __FUNCTION__, ++ sd->task->comm, sd->task->pid, ++ BASE_PROFILE(sd->active)->name, BASE_PROFILE(sd->active), ++ sd->active->name, sd->active); ++ ++ if (!sd->active) ++ goto out; ++ ++ if (IN_SUBPROFILE(sd->active)) { ++ struct aaprofile *nactive; ++ ++ /* The old profile was in a hat, check to see if the new ++ * profile has an equivalent hat */ ++ nactive = __aa_find_profile(sd->active->name, &new->sub); ++ ++ if (!nactive) ++ nactive = get_aaprofile(new->null_profile); ++ ++ aa_switch(sd, nactive); ++ put_aaprofile(nactive); ++ } else { ++ aa_switch(sd, new); ++ } ++ ++ out: ++ return; ++} ++ ++/** taskreplace_iter - Iterator to replace a subdomain's profile ++ * @sd: subdomain to consider for profile replacement ++ * @cookie: pointer to the old profile which is being replaced. ++ * ++ * If the subdomain's active profile matches old_profile call ++ * task_replace() to replace with the subdomain's active profile with ++ * the new profile. ++ */ ++static int taskreplace_iter(struct subdomain *sd, void *cookie) ++{ ++ struct aa_taskreplace_data *data = (struct aa_taskreplace_data *)cookie; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ ++ if (__aa_is_confined(sd) && ++ BASE_PROFILE(sd->active) == data->old_profile) ++ task_replace(sd, data->new_profile); ++ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++ return 0; ++} ++ ++static inline int aa_inbounds(struct aa_ext *e, size_t size) ++{ ++ return (e->pos + size <= e->end); ++} ++ ++/** ++ * aaconvert - convert trailing values of serialized type codes ++ * @code: type code ++ * @dest: pointer to object to receive the converted value ++ * @src: pointer to value to convert ++ * ++ * for serialized type codes which have a trailing value, convert it ++ * and place it in @dest. If a code does not have a trailing value nop. ++ */ ++static void aaconvert(enum aa_code code, void *dest, void *src) ++{ ++ switch (code) { ++ case AA_U8: ++ *(u8 *)dest = *(u8 *) src; ++ break; ++ case AA_U16: ++ case AA_NAME: ++ case AA_DYN_STRING: ++ *(u16 *)dest = le16_to_cpu(get_unaligned((u16 *)src)); ++ break; ++ case AA_U32: ++ case AA_STATIC_BLOB: ++ *(u32 *)dest = le32_to_cpu(get_unaligned((u32 *)src)); ++ break; ++ case AA_U64: ++ *(u64 *)dest = le64_to_cpu(get_unaligned((u64 *)src)); ++ break; ++ default: ++ /* nop - all other type codes do not have a trailing value */ ++ ; ++ } ++} ++ ++/** ++ * aa_is_X - check if the next element is of type X ++ * @e: serialized data extent information ++ * @code: type code ++ * @data: object located at @e->pos (of type @code) is written into @data ++ * if @data is non-null. if data is null it means skip this ++ * entry ++ * check to see if the next element in the serialized data stream is of type ++ * X and check that it is with in bounds, if so put the associated value in ++ * @data. ++ * return the size of bytes associated with the returned data ++ * for complex object like blob and string a pointer to the allocated ++ * data is returned in data, but the size of the blob or string is ++ * returned. ++ */ ++static u32 aa_is_X(struct aa_ext *e, enum aa_code code, void *data) ++{ ++ void *pos = e->pos; ++ int ret = 0; ++ if (!aa_inbounds(e, AA_CODE_BYTE + aacode_datasize[code])) ++ goto fail; ++ if (code != *(u8 *)e->pos) ++ goto out; ++ e->pos += AA_CODE_BYTE; ++ if (code == AA_NAME) { ++ u16 size; ++ /* name codes are followed by X bytes */ ++ size = le16_to_cpu(get_unaligned((u16 *)e->pos)); ++ if (!aa_inbounds(e, (size_t) size)) ++ goto fail; ++ if (data) ++ *(u16 *)data = size; ++ e->pos += aacode_datasize[code]; ++ ret = 1 + aacode_datasize[code]; ++ } else if (code == AA_DYN_STRING) { ++ u16 size; ++ char *str; ++ /* strings codes are followed by X bytes */ ++ size = le16_to_cpu(get_unaligned((u16 *)e->pos)); ++ e->pos += aacode_datasize[code]; ++ if (!aa_inbounds(e, (size_t) size)) ++ goto fail; ++ if (data) { ++ * (char **)data = NULL; ++ str = kmalloc(size, GFP_KERNEL); ++ if (!str) ++ goto fail; ++ memcpy(str, e->pos, (size_t) size); ++ str[size-1] = '\0'; ++ * (char **)data = str; ++ } ++ e->pos += size; ++ ret = size; ++ } else if (code == AA_STATIC_BLOB) { ++ u32 size; ++ /* blobs are followed by X bytes, that can be 2^32 */ ++ size = le32_to_cpu(get_unaligned((u32 *)e->pos)); ++ e->pos += aacode_datasize[code]; ++ if (!aa_inbounds(e, (size_t) size)) ++ goto fail; ++ if (data) ++ memcpy(data, e->pos, (size_t) size); ++ e->pos += size; ++ ret = size; ++ } else { ++ if (data) ++ aaconvert(code, data, e->pos); ++ e->pos += aacode_datasize[code]; ++ ret = 1 + aacode_datasize[code]; ++ } ++out: ++ return ret; ++fail: ++ e->pos = pos; ++ return 0; ++} ++ ++/** ++ * aa_is_nameX - check is the next element is of type X with a name of @name ++ * @e: serialized data extent information ++ * @code: type code ++ * @data: location to store deserialized data if match isX criteria ++ * @name: name to match to the serialized element. ++ * ++ * check that the next serialized data element is of type X and has a tag ++ * name @name. If the code matches and name (if specified) matches then ++ * the packed data is unpacked into *data. (Note for strings this is the ++ * size, and the next data in the stream is the string data) ++ * returns %0 if either match failes ++ */ ++static int aa_is_nameX(struct aa_ext *e, enum aa_code code, void *data, ++ const char *name) ++{ ++ void *pos = e->pos; ++ u16 size; ++ u32 ret; ++ /* check for presence of a tagname, and if present name size ++ * AA_NAME tag value is a u16 */ ++ if (aa_is_X(e, AA_NAME, &size)) { ++ /* if a name is specified it must match. otherwise skip tag */ ++ if (name && ((strlen(name) != size-1) || ++ strncmp(name, (char *)e->pos, (size_t)size-1))) ++ goto fail; ++ e->pos += size; ++ } ++ /* now check if data actually matches */ ++ ret = aa_is_X(e, code, data); ++ if (!ret) ++ goto fail; ++ return ret; ++ ++fail: ++ e->pos = pos; ++ return 0; ++} ++ ++/* macro to wrap error case to make a block of reads look nicer */ ++#define AA_READ_X(E, C, D, N) \ ++ do { \ ++ u32 __ret; \ ++ __ret = aa_is_nameX((E), (C), (D), (N)); \ ++ if (!__ret) \ ++ goto fail; \ ++ } while (0) ++ ++/** ++ * aa_activate_net_entry - unpacked serialized net entries ++ * @e: serialized data extent information ++ * ++ * Ignore/skips net entries if they are present in the serialized data ++ * stream. Network confinement rules are currently unsupported but some ++ * user side tools can generate them so they are currently ignored. ++ */ ++static inline int aa_activate_net_entry(struct aa_ext *e) ++{ ++ AA_READ_X(e, AA_STRUCT, NULL, "ne"); ++ AA_READ_X(e, AA_U32, NULL, NULL); ++ AA_READ_X(e, AA_U32, NULL, NULL); ++ AA_READ_X(e, AA_U32, NULL, NULL); ++ AA_READ_X(e, AA_U16, NULL, NULL); ++ AA_READ_X(e, AA_U16, NULL, NULL); ++ AA_READ_X(e, AA_U32, NULL, NULL); ++ AA_READ_X(e, AA_U32, NULL, NULL); ++ AA_READ_X(e, AA_U16, NULL, NULL); ++ AA_READ_X(e, AA_U16, NULL, NULL); ++ /* interface name is optional so just ignore return code */ ++ aa_is_nameX(e, AA_DYN_STRING, NULL, NULL); ++ AA_READ_X(e, AA_STRUCTEND, NULL, NULL); ++ ++ return 1; ++fail: ++ return 0; ++} ++ ++/** ++ * aa_activate_file_entry - unpack serialized file entry ++ * @e: serialized data extent information ++ * ++ * unpack the information used for a file ACL entry. ++ */ ++static inline struct aa_entry *aa_activate_file_entry(struct aa_ext *e) ++{ ++ struct aa_entry *entry = NULL; ++ ++ if (!(entry = alloc_aa_entry())) ++ goto fail; ++ ++ AA_READ_X(e, AA_STRUCT, NULL, "fe"); ++ AA_READ_X(e, AA_DYN_STRING, &entry->filename, NULL); ++ AA_READ_X(e, AA_U32, &entry->mode, "file.mode"); ++ AA_READ_X(e, AA_U32, &entry->type, "file.pattern_type"); ++ ++ entry->extradata = aamatch_alloc(entry->type); ++ if (IS_ERR(entry->extradata)) { ++ entry->extradata = NULL; ++ goto fail; ++ } ++ ++ if (entry->extradata && ++ aamatch_serialize(entry->extradata, e, aa_is_nameX) != 0) { ++ goto fail; ++ } ++ AA_READ_X(e, AA_STRUCTEND, NULL, NULL); ++ ++ switch (entry->type) { ++ case aa_entry_literal: ++ AA_DEBUG("%s: %s [no pattern] mode=0x%x\n", ++ __FUNCTION__, ++ entry->filename, ++ entry->mode); ++ break; ++ case aa_entry_tailglob: ++ AA_DEBUG("%s: %s [tailglob] mode=0x%x\n", ++ __FUNCTION__, ++ entry->filename, ++ entry->mode); ++ break; ++ case aa_entry_pattern: ++ AA_DEBUG("%s: %s mode=0x%x\n", ++ __FUNCTION__, ++ entry->filename, ++ entry->mode); ++ break; ++ default: ++ AA_WARN("%s: INVALID entry_match_type %d\n", ++ __FUNCTION__, ++ (int)entry->type); ++ goto fail; ++ } ++ ++ return entry; ++ ++fail: ++ aamatch_free(entry->extradata); ++ free_aa_entry(entry); ++ return NULL; ++} ++ ++/** ++ * check_rule_and_add - check a file rule is valid and add to a profile ++ * @file_entry: file rule to add ++ * @profile: profile to add the rule to ++ * @message: error message returned if the addition failes. ++ * ++ * perform consistency check to ensure that a file rule entry is valid. ++ * If the rule is valid it is added to the profile. ++ */ ++static inline int check_rule_and_add(struct aa_entry *file_entry, ++ struct aaprofile *profile, ++ const char **message) ++{ ++ /* verify consistency of x, px, ix, ux for entry against ++ possible duplicates for this entry */ ++ int mode = AA_EXEC_MODIFIER_MASK(file_entry->mode); ++ int i; ++ ++ if (mode && !(AA_MAY_EXEC & file_entry->mode)) { ++ *message = "inconsistent rule, x modifiers without x"; ++ goto out; ++ } ++ ++ /* check that only 1 of the modifiers is set */ ++ if (mode && (mode & (mode - 1))) { ++ *message = "inconsistent rule, multiple x modifiers"; ++ goto out; ++ } ++ ++ list_add(&file_entry->list, &profile->file_entry); ++ profile->num_file_entries++; ++ ++ mode = file_entry->mode; ++ ++ /* Handle partitioned lists ++ * Chain entries onto sublists based on individual ++ * permission bits. This allows more rapid searching. ++ */ ++ for (i = 0; i <= POS_AA_FILE_MAX; i++) { ++ if (mode & (1 << i)) ++ /* profile->file_entryp[i] initially set to ++ * NULL in alloc_aaprofile() */ ++ list_add(&file_entry->listp[i], ++ &profile->file_entryp[i]); ++ } ++ ++ return 1; ++ ++out: ++ free_aa_entry(file_entry); ++ return 0; ++} ++ ++#define AA_ENTRY_LIST(NAME) \ ++ do { \ ++ if (aa_is_nameX(e, AA_LIST, NULL, (NAME))) { \ ++ rulename = ""; \ ++ error_string = "Invalid file entry"; \ ++ while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) { \ ++ struct aa_entry *file_entry; \ ++ file_entry = aa_activate_file_entry(e); \ ++ if (!file_entry) \ ++ goto fail; \ ++ if (!check_rule_and_add(file_entry, profile, \ ++ &error_string)) { \ ++ rulename = file_entry->filename; \ ++ goto fail; \ ++ } \ ++ } \ ++ } \ ++ } while (0) ++ ++/** ++ * aa_activate_profile - unpack a serialized profile ++ * @e: serialized data extent information ++ * @error: error code returned if unpacking fails ++ */ ++static struct aaprofile *aa_activate_profile(struct aa_ext *e, ssize_t *error) ++{ ++ struct aaprofile *profile = NULL; ++ const char *rulename = ""; ++ const char *error_string = "Invalid Profile"; ++ ++ *error = -EPROTO; ++ ++ profile = alloc_aaprofile(); ++ if (!profile) { ++ error_string = "Could not allocate profile"; ++ *error = -ENOMEM; ++ goto fail; ++ } ++ ++ /* check that we have the right struct being passed */ ++ AA_READ_X(e, AA_STRUCT, NULL, "profile"); ++ AA_READ_X(e, AA_DYN_STRING, &profile->name, NULL); ++ ++ error_string = "Invalid flags"; ++ /* per profile debug flags (debug, complain, audit) */ ++ AA_READ_X(e, AA_STRUCT, NULL, "flags"); ++ AA_READ_X(e, AA_U32, &(profile->flags.debug), "profile.flags.debug"); ++ AA_READ_X(e, AA_U32, &(profile->flags.complain), ++ "profile.flags.complain"); ++ AA_READ_X(e, AA_U32, &(profile->flags.audit), "profile.flags.audit"); ++ AA_READ_X(e, AA_STRUCTEND, NULL, NULL); ++ ++ error_string = "Invalid capabilities"; ++ AA_READ_X(e, AA_U32, &(profile->capabilities), "profile.capabilities"); ++ ++ /* get the file entries. */ ++ AA_ENTRY_LIST("pgent"); /* pcre rules */ ++ AA_ENTRY_LIST("sgent"); /* simple globs */ ++ AA_ENTRY_LIST("fent"); /* regular file entries */ ++ ++ /* get the net entries */ ++ if (aa_is_nameX(e, AA_LIST, NULL, "net")) { ++ error_string = "Invalid net entry"; ++ while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) { ++ if (!aa_activate_net_entry(e)) ++ goto fail; ++ } ++ } ++ rulename = ""; ++ ++ /* get subprofiles */ ++ if (aa_is_nameX(e, AA_LIST, NULL, "hats")) { ++ error_string = "Invalid profile hat"; ++ while (!aa_is_nameX(e, AA_LISTEND, NULL, NULL)) { ++ struct aaprofile *subprofile; ++ subprofile = aa_activate_profile(e, error); ++ if (!subprofile) ++ goto fail; ++ subprofile->parent = profile; ++ list_add(&subprofile->list, &profile->sub); ++ } ++ } ++ ++ error_string = "Invalid end of profile"; ++ AA_READ_X(e, AA_STRUCTEND, NULL, NULL); ++ ++ return profile; ++ ++fail: ++ AA_WARN("%s: %s %s in profile %s\n", INTERFACE_ID, rulename, ++ error_string, profile && profile->name ? profile->name ++ : "unknown"); ++ ++ if (profile) { ++ free_aaprofile(profile); ++ profile = NULL; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * aa_activate_top_profile - unpack a serialized base profile ++ * @e: serialized data extent information ++ * @error: error code returned if unpacking fails ++ * ++ * check interface version unpack a profile and all its hats and patch ++ * in any extra information that the profile needs. ++ */ ++static void *aa_activate_top_profile(struct aa_ext *e, ssize_t *error) ++{ ++ struct aaprofile *profile = NULL; ++ ++ /* get the interface version */ ++ if (!aa_is_nameX(e, AA_U32, &e->version, "version")) { ++ AA_WARN("%s: version missing\n", INTERFACE_ID); ++ *error = -EPROTONOSUPPORT; ++ goto fail; ++ } ++ ++ /* check that the interface version is currently supported */ ++ if (e->version != 2) { ++ AA_WARN("%s: unsupported interface version (%d)\n", ++ INTERFACE_ID, e->version); ++ *error = -EPROTONOSUPPORT; ++ goto fail; ++ } ++ ++ profile = aa_activate_profile(e, error); ++ if (!profile) ++ goto fail; ++ ++ if (!list_empty(&profile->sub) || profile->flags.complain) { ++ if (attach_nullprofile(profile)) ++ goto fail; ++ } ++ return profile; ++ ++fail: ++ free_aaprofile(profile); ++ return NULL; ++} ++ ++/** ++ * aa_file_prof_add - add a new profile to the profile list ++ * @data: serialized data stream ++ * @size: size of the serialized data stream ++ * ++ * unpack and add a profile to the profile list. Return %0 or error ++ */ ++ssize_t aa_file_prof_add(void *data, size_t size) ++{ ++ struct aaprofile *profile = NULL; ++ ++ struct aa_ext e = { ++ .start = data, ++ .end = data + size, ++ .pos = data ++ }; ++ ssize_t error; ++ ++ profile = aa_activate_top_profile(&e, &error); ++ if (!profile) { ++ AA_DEBUG("couldn't activate profile\n"); ++ goto out; ++ } ++ ++ /* aa_activate_top_profile allocates profile with initial 1 count ++ * aa_profilelist_add transfers that ref to profile list without ++ * further incrementing ++ */ ++ if (aa_profilelist_add(profile)) { ++ error = size; ++ } else { ++ AA_WARN("trying to add profile (%s) that already exists.\n", ++ profile->name); ++ put_aaprofile(profile); ++ error = -EEXIST; ++ } ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_file_prof_repl - replace a profile on the profile list ++ * @udata: serialized data stream ++ * @size: size of the serialized data stream ++ * ++ * unpack and replace a profile on the profile list and uses of that profile ++ * by any subdomain. If the profile does not exist on the profile list ++ * it is added. Return %0 or error. ++ */ ++ssize_t aa_file_prof_repl(void *udata, size_t size) ++{ ++ struct aa_taskreplace_data data; ++ struct aa_ext e = { ++ .start = udata, ++ .end = udata + size, ++ .pos = udata ++ }; ++ ++ ssize_t error; ++ ++ data.new_profile = aa_activate_top_profile(&e, &error); ++ if (!data.new_profile) { ++ AA_DEBUG("couldn't activate profile\n"); ++ goto out; ++ } ++ ++ /* Refcount on data.new_profile is 1 (aa_activate_top_profile). ++ * ++ * This reference will be inherited by aa_profilelist_replace for it's ++ * profile list reference but this isn't sufficient. ++ * ++ * Another replace (*for-same-profile*) may race us here. ++ * Task A calls aa_profilelist_replace(new_profile) and is interrupted. ++ * Task B old_profile = aa_profilelist_replace() will return task A's ++ * new_profile with the count of 1. If task B proceeeds to put this ++ * profile it will dissapear from under task A. ++ * ++ * Grab extra reference on new_profile to prevent this ++ */ ++ ++ get_aaprofile(data.new_profile); ++ ++ data.old_profile = aa_profilelist_replace(data.new_profile); ++ ++ /* If there was an old profile, find all currently executing tasks ++ * using this profile and replace the old profile with the new. ++ */ ++ if (data.old_profile) { ++ AA_DEBUG("%s: try to replace profile (%p)%s\n", ++ __FUNCTION__, ++ data.old_profile, ++ data.old_profile->name); ++ ++ aa_subdomainlist_iterate(taskreplace_iter, (void *)&data); ++ ++ /* it's off global list, and we are done replacing */ ++ put_aaprofile(data.old_profile); ++ } ++ ++ /* release extra reference obtained above (race) */ ++ put_aaprofile(data.new_profile); ++ ++ error = size; ++ ++out: ++ return error; ++} ++ ++/** ++ * aa_file_prof_remove - remove a profile from the system ++ * @name: name of the profile to remove ++ * @size: size of the name ++ * ++ * remove a profile from the profile list and all subdomain references ++ * to said profile. Return %0 on success, else error. ++ */ ++ssize_t aa_file_prof_remove(const char *name, size_t size) ++{ ++ struct aaprofile *old_profile; ++ ++ /* if the old profile exists it will be removed from the list and ++ * a reference is returned. ++ */ ++ old_profile = aa_profilelist_remove(name); ++ ++ if (old_profile) { ++ /* remove profile from any tasks using it */ ++ aa_subdomainlist_iterate(taskremove_iter, (void *)old_profile); ++ ++ /* drop reference obtained by aa_profilelist_remove */ ++ put_aaprofile(old_profile); ++ } else { ++ AA_WARN("%s: trying to remove profile (%s) that " ++ "doesn't exist - skipping.\n", __FUNCTION__, name); ++ return -ENOENT; ++ } ++ ++ return size; ++} ++ ++/** ++ * free_aaprofile_kref - free aaprofile by kref (called by put_aaprofile) ++ * @kr: kref callback for freeing of a profile ++ */ ++void free_aaprofile_kref(struct kref *kr) ++{ ++ struct aaprofile *p=container_of(kr, struct aaprofile, count); ++ ++ call_rcu(&p->rcu, free_aaprofile_rcu); ++} ++ ++/** ++ * free_aaprofile - free aaprofile structure ++ * @profile: the profile to free ++ * ++ * free a profile, its file entries hats and null_profile. All references ++ * to the profile, its hats and null_profile must have been put. ++ * If the profile was referenced by a subdomain free_aaprofile should be ++ * called from an rcu callback routine. ++ */ ++void free_aaprofile(struct aaprofile *profile) ++{ ++ struct aa_entry *ent, *tmp; ++ struct aaprofile *p, *ptmp; ++ ++ AA_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ ++ if (!profile) ++ return; ++ ++ /* profile is still on global profile list -- invalid */ ++ if (!list_empty(&profile->list)) { ++ AA_ERROR("%s: internal error, " ++ "profile '%s' still on global list\n", ++ __FUNCTION__, ++ profile->name); ++ BUG(); ++ } ++ ++ list_for_each_entry_safe(ent, tmp, &profile->file_entry, list) { ++ if (ent->filename) ++ AA_DEBUG("freeing aa_entry: %p %s\n", ++ ent->filename, ent->filename); ++ list_del_init(&ent->list); ++ free_aa_entry(ent); ++ } ++ ++ /* use free_aaprofile instead of put_aaprofile to destroy the ++ * null_profile, because the null_profile use the same reference ++ * counting as hats, ie. the count goes to the base profile. ++ */ ++ free_aaprofile(profile->null_profile); ++ list_for_each_entry_safe(p, ptmp, &profile->sub, list) { ++ list_del_init(&p->list); ++ p->parent = NULL; ++ put_aaprofile(p); ++ } ++ ++ if (profile->name) { ++ AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name); ++ kfree(profile->name); ++ } ++ ++ kfree(profile); ++} +--- security/apparmor/module_interface.h.orig ++++ security/apparmor/module_interface.h +@@ -0,0 +1,37 @@ ++#ifndef __MODULEINTERFACE_H ++#define __MODULEINTERFACE_H ++ ++/* Codes of the types of basic structures that are understood */ ++#define AA_CODE_BYTE (sizeof(u8)) ++#define INTERFACE_ID "INTERFACE" ++ ++#define SUBDOMAIN_INTERFACE_VERSION 2 ++ ++enum aa_code { ++ AA_U8, ++ AA_U16, ++ AA_U32, ++ AA_U64, ++ AA_NAME, /* same as string except it is items name */ ++ AA_DYN_STRING, ++ AA_STATIC_BLOB, ++ AA_STRUCT, ++ AA_STRUCTEND, ++ AA_LIST, ++ AA_LISTEND, ++ AA_OFFSET, ++ AA_BAD ++}; ++ ++/* aa_ext tracks the kernel buffer and read position in it. The interface ++ * data is copied into a kernel buffer in apparmorfs and then handed off to ++ * the activate routines. ++ */ ++struct aa_ext { ++ void *start; ++ void *end; ++ void *pos; /* pointer to current position in the buffer */ ++ u32 version; ++}; ++ ++#endif /* __MODULEINTERFACE_H */ +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5006_apparmor-misc.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5006_apparmor-misc.patch new file mode 100644 index 0000000..9336bda --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5006_apparmor-misc.patch @@ -0,0 +1,681 @@ +This patch implements three distinct chunks. +- list management, for profiles loaded into the system (profile_list) and for + the set of confined tasks (subdomain_list) +- the proc/pid/attr interface used by userspace for setprofile (forcing + a task into a new profile) and changehat (switching a task into one of it's + defined sub profiles). Access to change_hat is normally via code provided + in libapparmor. See the overview posting for more information in change hat. +- capability utility functions (for displaying capability names) + + +Signed-off-by: Tony Jones + +--- + security/apparmor/capabilities.c | 54 ++++++ + security/apparmor/list.c | 268 +++++++++++++++++++++++++++++++ + security/apparmor/procattr.c | 327 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 649 insertions(+) + +--- security/apparmor/capabilities.c.orig ++++ security/apparmor/capabilities.c +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor capability definitions ++ */ ++ ++#include "apparmor.h" ++ ++static const char *cap_names[] = { ++ "chown", ++ "dac_override", ++ "dac_read_search", ++ "fowner", ++ "fsetid", ++ "kill", ++ "setgid", ++ "setuid", ++ "setpcap", ++ "linux_immutable", ++ "net_bind_service", ++ "net_broadcast", ++ "net_admin", ++ "net_raw", ++ "ipc_lock", ++ "ipc_owner", ++ "sys_module", ++ "sys_rawio", ++ "sys_chroot", ++ "sys_ptrace", ++ "sys_pacct", ++ "sys_admin", ++ "sys_boot", ++ "sys_nice", ++ "sys_resource", ++ "sys_time", ++ "sys_tty_config", ++ "mknod", ++ "lease" ++}; ++ ++const char *capability_to_name(unsigned int cap) ++{ ++ const char *name; ++ ++ name = (cap < (sizeof(cap_names) / sizeof(char *)) ++ ? cap_names[cap] : "invalid-capability"); ++ ++ return name; ++} +--- security/apparmor/list.c.orig ++++ security/apparmor/list.c +@@ -0,0 +1,268 @@ ++/* ++ * Copyright (C) 1998-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor Profile List Management ++ */ ++ ++#include ++#include "apparmor.h" ++#include "inline.h" ++ ++/* list of all profiles and lock */ ++static LIST_HEAD(profile_list); ++static rwlock_t profile_lock = RW_LOCK_UNLOCKED; ++ ++/* list of all subdomains and lock */ ++static LIST_HEAD(subdomain_list); ++static rwlock_t subdomain_lock = RW_LOCK_UNLOCKED; ++ ++/** ++ * aa_profilelist_find ++ * @name: profile name (program name) ++ * ++ * Search the profile list for profile @name. Return refcounted profile on ++ * success, NULL on failure. ++ */ ++struct aaprofile *aa_profilelist_find(const char *name) ++{ ++ struct aaprofile *p = NULL; ++ if (name) { ++ read_lock(&profile_lock); ++ p = __aa_find_profile(name, &profile_list); ++ read_unlock(&profile_lock); ++ } ++ return p; ++} ++ ++/** ++ * aa_profilelist_add - add new profile to list ++ * @profile: new profile to add to list ++ * ++ * NOTE: Caller must allocate necessary reference count that will be used ++ * by the profile_list. This is because profile allocation alloc_aaprofile() ++ * returns an unreferenced object with a initial count of %1. ++ * ++ * Return %1 on success, %0 on failure (already exists) ++ */ ++int aa_profilelist_add(struct aaprofile *profile) ++{ ++ struct aaprofile *old_profile; ++ int ret = 0; ++ ++ if (!profile) ++ goto out; ++ ++ write_lock(&profile_lock); ++ old_profile = __aa_find_profile(profile->name, &profile_list); ++ if (old_profile) { ++ put_aaprofile(old_profile); ++ goto out; ++ } ++ ++ list_add(&profile->list, &profile_list); ++ ret = 1; ++ out: ++ write_unlock(&profile_lock); ++ return ret; ++} ++ ++/** ++ * aa_profilelist_remove - remove a profile from the list by name ++ * @name: name of profile to be removed ++ * ++ * If the profile exists remove profile from list and return its reference. ++ * The reference count on profile is not decremented and should be decremented ++ * when the profile is no longer needed ++ */ ++struct aaprofile *aa_profilelist_remove(const char *name) ++{ ++ struct aaprofile *profile = NULL; ++ struct aaprofile *p, *tmp; ++ ++ if (!name) ++ goto out; ++ ++ write_lock(&profile_lock); ++ list_for_each_entry_safe(p, tmp, &profile_list, list) { ++ if (!strcmp(p->name, name)) { ++ list_del_init(&p->list); ++ /* mark old profile as stale */ ++ p->isstale = 1; ++ profile = p; ++ break; ++ } ++ } ++ write_unlock(&profile_lock); ++ ++out: ++ return profile; ++} ++ ++/** ++ * aa_profilelist_replace - replace a profile on the list ++ * @profile: new profile ++ * ++ * Replace a profile on the profile list. Find the old profile by name in ++ * the list, and replace it with the new profile. NOTE: Caller must allocate ++ * necessary initial reference count for new profile as aa_profilelist_add(). ++ * ++ * This is an atomic list operation. Returns the old profile (which is still ++ * refcounted) if there was one, or NULL. ++ */ ++struct aaprofile *aa_profilelist_replace(struct aaprofile *profile) ++{ ++ struct aaprofile *oldprofile; ++ ++ write_lock(&profile_lock); ++ oldprofile = __aa_find_profile(profile->name, &profile_list); ++ if (oldprofile) { ++ list_del_init(&oldprofile->list); ++ /* mark old profile as stale */ ++ oldprofile->isstale = 1; ++ ++ /* __aa_find_profile incremented count, so adjust down */ ++ put_aaprofile(oldprofile); ++ } ++ ++ list_add(&profile->list, &profile_list); ++ write_unlock(&profile_lock); ++ ++ return oldprofile; ++} ++ ++/** ++ * aa_profilelist_release - Remove all profiles from profile_list ++ */ ++void aa_profilelist_release(void) ++{ ++ struct aaprofile *p, *tmp; ++ ++ write_lock(&profile_lock); ++ list_for_each_entry_safe(p, tmp, &profile_list, list) { ++ list_del_init(&p->list); ++ put_aaprofile(p); ++ } ++ write_unlock(&profile_lock); ++} ++ ++/** ++ * aa_subdomainlist_add - Add subdomain to subdomain_list ++ * @sd: new subdomain ++ */ ++void aa_subdomainlist_add(struct subdomain *sd) ++{ ++ unsigned long flags; ++ ++ if (!sd) { ++ AA_INFO("%s: bad subdomain\n", __FUNCTION__); ++ return; ++ } ++ ++ write_lock_irqsave(&subdomain_lock, flags); ++ /* new subdomains must be added to the end of the list due to a ++ * subtle interaction between fork and profile replacement. ++ */ ++ list_add_tail(&sd->list, &subdomain_list); ++ write_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/** ++ * aa_subdomainlist_remove - Remove subdomain from subdomain_list ++ * @sd: subdomain to be removed ++ */ ++void aa_subdomainlist_remove(struct subdomain *sd) ++{ ++ unsigned long flags; ++ ++ if (sd) { ++ write_lock_irqsave(&subdomain_lock, flags); ++ list_del_init(&sd->list); ++ write_unlock_irqrestore(&subdomain_lock, flags); ++ } ++} ++ ++/** ++ * aa_subdomainlist_iterate - iterate over the subdomain list applying @func ++ * @func: method to be called for each element ++ * @cookie: user passed data ++ * ++ * Iterate over subdomain list applying @func, stop when @func returns ++ * non zero ++ */ ++void aa_subdomainlist_iterate(aa_iter func, void *cookie) ++{ ++ struct subdomain *node; ++ int ret = 0; ++ unsigned long flags; ++ ++ read_lock_irqsave(&subdomain_lock, flags); ++ list_for_each_entry(node, &subdomain_list, list) { ++ ret = (*func) (node, cookie); ++ if (ret != 0) ++ break; ++ } ++ read_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/** ++ * aa_subdomainlist_release - Remove all subdomains from subdomain_list ++ */ ++void aa_subdomainlist_release() ++{ ++ struct subdomain *node, *tmp; ++ unsigned long flags; ++ ++ write_lock_irqsave(&subdomain_lock, flags); ++ list_for_each_entry_safe(node, tmp, &subdomain_list, list) { ++ list_del_init(&node->list); ++ } ++ write_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/* seq_file helper routines ++ * Used by apparmorfs.c to iterate over profile_list ++ */ ++static void *p_start(struct seq_file *f, loff_t *pos) ++{ ++ struct aaprofile *node; ++ loff_t l = *pos; ++ ++ read_lock(&profile_lock); ++ list_for_each_entry(node, &profile_list, list) ++ if (!l--) ++ return node; ++ return NULL; ++} ++ ++static void *p_next(struct seq_file *f, void *p, loff_t *pos) ++{ ++ struct list_head *lh = ((struct aaprofile *)p)->list.next; ++ (*pos)++; ++ return lh == &profile_list ? ++ NULL : list_entry(lh, struct aaprofile, list); ++} ++ ++static void p_stop(struct seq_file *f, void *v) ++{ ++ read_unlock(&profile_lock); ++} ++ ++static int seq_show_profile(struct seq_file *f, void *v) ++{ ++ struct aaprofile *profile = (struct aaprofile *)v; ++ seq_printf(f, "%s (%s)\n", profile->name, ++ PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); ++ return 0; ++} ++ ++struct seq_operations apparmorfs_profiles_op = { ++ .start = p_start, ++ .next = p_next, ++ .stop = p_stop, ++ .show = seq_show_profile, ++}; +--- security/apparmor/procattr.c.orig ++++ security/apparmor/procattr.c +@@ -0,0 +1,327 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor /proc/pid/attr handling ++ */ ++ ++/* for isspace */ ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++size_t aa_getprocattr(struct aaprofile *active, char *str, size_t size) ++{ ++ int error = -EACCES; /* default to a perm denied */ ++ size_t len; ++ ++ if (active) { ++ size_t lena, lenm, lenp = 0; ++ const char *enforce_str = " (enforce)"; ++ const char *complain_str = " (complain)"; ++ const char *mode_str = ++ PROFILE_COMPLAIN(active) ? complain_str : enforce_str; ++ ++ lenm = strlen(mode_str); ++ ++ lena = strlen(active->name); ++ ++ len = lena; ++ if (IN_SUBPROFILE(active)) { ++ lenp = strlen(BASE_PROFILE(active)->name); ++ len += (lenp + 1); /* +1 for ^ */ ++ } ++ /* DONT null terminate strings we output via proc */ ++ len += (lenm + 1); /* for \n */ ++ ++ if (len <= size) { ++ if (lenp) { ++ memcpy(str, BASE_PROFILE(active)->name, ++ lenp); ++ str += lenp; ++ *str++ = '^'; ++ } ++ ++ memcpy(str, active->name, lena); ++ str += lena; ++ memcpy(str, mode_str, lenm); ++ str += lenm; ++ *str++ = '\n'; ++ error = len; ++ } else { ++ error = -ERANGE; ++ } ++ } else { ++ const char *unconstrained_str = "unconstrained\n"; ++ len = strlen(unconstrained_str); ++ ++ /* DONT null terminate strings we output via proc */ ++ if (len <= size) { ++ memcpy(str, unconstrained_str, len); ++ error = len; ++ } else { ++ error = -ERANGE; ++ } ++ } ++ ++ return error; ++ ++} ++ ++int aa_setprocattr_changehat(char *hatinfo, size_t infosize) ++{ ++ int error = -EINVAL; ++ char *token = NULL, *hat, *smagic, *tmp; ++ u32 magic; ++ int rc, len, consumed; ++ unsigned long flags; ++ ++ AA_DEBUG("%s: %p %zd\n", __FUNCTION__, hatinfo, infosize); ++ ++ /* strip leading white space */ ++ while (infosize && isspace(*hatinfo)) { ++ hatinfo++; ++ infosize--; ++ } ++ ++ if (infosize == 0) ++ goto out; ++ ++ /* ++ * Copy string to a new buffer so we can play with it ++ * It may be zero terminated but we add a trailing 0 ++ * for 100% safety ++ */ ++ token = kmalloc(infosize + 1, GFP_KERNEL); ++ ++ if (!token) { ++ error = -ENOMEM; ++ goto out; ++ } ++ ++ memcpy(token, hatinfo, infosize); ++ token[infosize] = 0; ++ ++ /* error is INVAL until we have at least parsed something */ ++ error = -EINVAL; ++ ++ tmp = token; ++ while (*tmp && *tmp != '^') { ++ tmp++; ++ } ++ ++ if (!*tmp || tmp == token) { ++ AA_WARN("%s: Invalid input '%s'\n", __FUNCTION__, token); ++ goto out; ++ } ++ ++ /* split magic and hat into two strings */ ++ *tmp = 0; ++ smagic = token; ++ ++ /* ++ * Initially set consumed=strlen(magic), as if sscanf ++ * consumes all input via the %x it will not process the %n ++ * directive. Otherwise, if sscanf does not consume all the ++ * input it will process the %n and update consumed. ++ */ ++ consumed = len = strlen(smagic); ++ ++ rc = sscanf(smagic, "%x%n", &magic, &consumed); ++ ++ if (rc != 1 || consumed != len) { ++ AA_WARN("%s: Invalid hex magic %s\n", ++ __FUNCTION__, ++ smagic); ++ goto out; ++ } ++ ++ hat = tmp + 1; ++ ++ if (!*hat) ++ hat = NULL; ++ ++ if (!hat && !magic) { ++ AA_WARN("%s: Invalid input, NULL hat and NULL magic\n", ++ __FUNCTION__); ++ goto out; ++ } ++ ++ AA_DEBUG("%s: Magic 0x%x Hat '%s'\n", ++ __FUNCTION__, magic, hat ? hat : NULL); ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ error = aa_change_hat(hat, magic); ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++out: ++ if (token) { ++ memset(token, 0, infosize); ++ kfree(token); ++ } ++ ++ return error; ++} ++ ++int aa_setprocattr_setprofile(struct task_struct *p, char *profilename, ++ size_t profilesize) ++{ ++ int error = -EINVAL; ++ struct aaprofile *profile = NULL; ++ struct subdomain *sd; ++ char *name = NULL; ++ unsigned long flags; ++ ++ AA_DEBUG("%s: current %s(%d)\n", ++ __FUNCTION__, current->comm, current->pid); ++ ++ /* strip leading white space */ ++ while (profilesize && isspace(*profilename)) { ++ profilename++; ++ profilesize--; ++ } ++ ++ if (profilesize == 0) ++ goto out; ++ ++ /* ++ * Copy string to a new buffer so we guarantee it is zero ++ * terminated ++ */ ++ name = kmalloc(profilesize + 1, GFP_KERNEL); ++ ++ if (!name) { ++ error = -ENOMEM; ++ goto out; ++ } ++ ++ strncpy(name, profilename, profilesize); ++ name[profilesize] = 0; ++ ++ repeat: ++ if (strcmp(name, "unconstrained") != 0) { ++ profile = aa_profilelist_find(name); ++ if (!profile) { ++ AA_WARN("%s: Unable to switch task %s(%d) to profile" ++ "'%s'. No such profile.\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ name); ++ ++ error = -EINVAL; ++ goto out; ++ } ++ } ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ ++ sd = AA_SUBDOMAIN(p->security); ++ ++ /* switch to unconstrained */ ++ if (!profile) { ++ if (__aa_is_confined(sd)) { ++ AA_WARN("%s: Unconstraining task %s(%d) " ++ "profile %s active %s\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ BASE_PROFILE(sd->active)->name, ++ sd->active->name); ++ ++ aa_switch_unconfined(sd); ++ } else { ++ AA_WARN("%s: task %s(%d) " ++ "is already unconstrained\n", ++ __FUNCTION__, p->comm, p->pid); ++ } ++ } else { ++ if (!sd) { ++ /* this task was created before module was ++ * loaded, allocate a subdomain ++ */ ++ AA_WARN("%s: task %s(%d) has no subdomain\n", ++ __FUNCTION__, p->comm, p->pid); ++ ++ /* unlock so we can safely GFP_KERNEL */ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++ sd = alloc_subdomain(p); ++ if (!sd) { ++ AA_WARN("%s: Unable to allocate subdomain for " ++ "task %s(%d). Cannot confine task to " ++ "profile %s\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ name); ++ ++ error = -ENOMEM; ++ put_aaprofile(profile); ++ ++ goto out; ++ } ++ ++ spin_lock_irqsave(&sd_lock, flags); ++ if (!AA_SUBDOMAIN(p->security)) { ++ p->security = sd; ++ } else { /* race */ ++ free_subdomain(sd); ++ sd = AA_SUBDOMAIN(p->security); ++ } ++ } ++ ++ /* ensure the profile hasn't been replaced */ ++ ++ if (unlikely(profile->isstale)) { ++ WARN_ON(profile == null_complain_profile); ++ ++ /* drop refcnt obtained from earlier get_aaprofile */ ++ put_aaprofile(profile); ++ profile = aa_profilelist_find(name); ++ ++ if (!profile) { ++ /* Race, profile was removed. */ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ goto repeat; ++ } ++ } ++ ++ /* we do not do a normal task replace since we are not ++ * replacing with the same profile. ++ * If existing process is in a hat, it will be moved ++ * into the new parent profile, even if this new ++ * profile has a identical named hat. ++ */ ++ ++ AA_WARN("%s: Switching task %s(%d) " ++ "profile %s active %s to new profile %s\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ sd->active ? BASE_PROFILE(sd->active)->name : ++ "unconstrained", ++ sd->active ? sd->active->name : "unconstrained", ++ name); ++ ++ aa_switch(sd, profile); ++ ++ put_aaprofile(profile); /* drop ref we obtained above ++ * from aa_profilelist_find ++ */ ++ ++ /* Reset magic in case we were in a subhat before ++ * This is the only case where we zero the magic after ++ * calling aa_switch ++ */ ++ sd->hat_magic = 0; ++ } ++ ++ spin_unlock_irqrestore(&sd_lock, flags); ++ ++out: ++ kfree(name); ++ ++ return error; ++} +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5007_apparmor-pathname-matching-submodule.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5007_apparmor-pathname-matching-submodule.patch new file mode 100644 index 0000000..a51d9c0 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5007_apparmor-pathname-matching-submodule.patch @@ -0,0 +1,246 @@ +The file match.h specifies a sub module interface consisting of the following +functions: + + aamatch_alloc + aamatch_free + Allocates/deallocates submodule specific data used by each + loaded profile (policy). + + aamatch_features + Returns the list of features implemented by the submodule. + These are literal, tailglob ("/path/**" match all paths + below /path) and pattern (full shell based pathname expansion). + + aamatch_serialize + Called by the module interface to serialize submodule specific + data from userspace. + + aamatch_match + Called to perform matching on a generated pathname. + + +The submodule submitted here implements only "literal" and "tailglob". +The version included with SuSE Linux implements "pattern" but via a method +that is not acceptable for mainline inclusion. We plan on developing +a new submodule as soon as possible that will implement the missing +functionality of the SuSE release using the textsearch framework and +a new bounded textsearch algorithm acceptable for subsequent inclusion +into the mainline kernel. + + +Signed-off-by: Tony Jones + +--- + security/apparmor/match/Makefile | 5 + + security/apparmor/match/match.h | 132 ++++++++++++++++++++++++++++++++ + security/apparmor/match/match_default.c | 57 +++++++++++++ + 3 files changed, 194 insertions(+) + +--- security/apparmor/match/Makefile.orig ++++ security/apparmor/match/Makefile +@@ -0,0 +1,5 @@ ++# Makefile for AppArmor aamatch submodule ++# ++obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_default.o ++ ++aamatch_default-y := match_default.o +--- security/apparmor/match/match.h.orig ++++ security/apparmor/match/match.h +@@ -0,0 +1,132 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor submodule (match) prototypes ++ */ ++ ++#ifndef __MATCH_H ++#define __MATCH_H ++ ++#include "../module_interface.h" ++#include "../apparmor.h" ++ ++/* The following functions implement an interface used by the primary ++ * AppArmor module to perform name matching (n.b. "AppArmor" was previously ++ * called "SubDomain"). ++ ++ * aamatch_alloc ++ * aamatch_free ++ * aamatch_features ++ * aamatch_serialize ++ * aamatch_match ++ * ++ * The intent is for the primary module to export (via virtual fs entries) ++ * the features provided by the submodule (aamatch_features) so that the ++ * parser may only load policy that can be supported. ++ * ++ * The primary module will call aamatch_serialize to allow the submodule ++ * to consume submodule specific data from parser data stream and will call ++ * aamatch_match to determine if a pathname matches an aa_entry. ++ */ ++ ++typedef int (*aamatch_serializecb) ++ (struct aa_ext *, enum aa_code, void *, const char *); ++ ++/** ++ * aamatch_alloc: allocate extradata (if necessary) ++ * @type: type of entry being allocated ++ * Return value: NULL indicates no data was allocated (ERR_PTR(x) on error) ++ */ ++extern void* aamatch_alloc(enum entry_match_type type); ++ ++/** ++ * aamatch_free: release data allocated by aamatch_alloc ++ * @entry_extradata: data previously allocated by aamatch_alloc ++ */ ++extern void aamatch_free(void *entry_extradata); ++ ++/** ++ * aamatch_features: return match types supported ++ * Return value: space seperated string (of types supported - use type=value ++ * to indicate variants of a type) ++ */ ++extern const char* aamatch_features(void); ++ ++/** ++ * aamatch_serialize: serialize extradata ++ * @entry_extradata: data previously allocated by aamatch_alloc ++ * @e: input stream ++ * @cb: callback fn (consume incoming data stream) ++ * Return value: 0 success, -ve error ++ */ ++extern int aamatch_serialize(void *entry_extradata, struct aa_ext *e, ++ aamatch_serializecb cb); ++ ++/** ++ * aamatch_match: determine if pathname matches entry ++ * @pathname: pathname to verify ++ * @entry_name: entry name ++ * @type: type of entry ++ * @entry_extradata: data previously allocated by aamatch_alloc ++ * Return value: 1 match, 0 othersise ++ */ ++extern unsigned int aamatch_match(const char *pathname, const char *entry_name, ++ enum entry_match_type type, ++ void *entry_extradata); ++ ++ ++/** ++ * sd_getmatch_type - return string representation of entry_match_type ++ * @type: entry match type ++ */ ++static inline const char *sd_getmatch_type(enum entry_match_type type) ++{ ++ const char *names[] = { ++ "aa_entry_literal", ++ "aa_entry_tailglob", ++ "aa_entry_pattern", ++ "aa_entry_invalid" ++ }; ++ ++ if (type >= aa_entry_invalid) { ++ type = aa_entry_invalid; ++ } ++ ++ return names[type]; ++} ++ ++/** ++ * aamatch_match_common - helper function to check if a pathname matches ++ * a literal/tailglob ++ * @path: path requested to search for ++ * @entry_name: name from aa_entry ++ * @type: type of entry ++ */ ++static inline int aamatch_match_common(const char *path, ++ const char *entry_name, ++ enum entry_match_type type) ++{ ++ int retval; ++ ++ /* literal, no pattern matching characters */ ++ if (type == aa_entry_literal) { ++ retval = (strcmp(entry_name, path) == 0); ++ /* trailing ** glob pattern */ ++ } else if (type == aa_entry_tailglob) { ++ retval = (strncmp(entry_name, path, ++ strlen(entry_name) - 2) == 0); ++ } else { ++ AA_WARN("%s: Invalid entry_match_type %d\n", ++ __FUNCTION__, type); ++ retval = 0; ++ } ++ ++ return retval; ++} ++ ++#endif /* __MATCH_H */ +--- security/apparmor/match/match_default.c.orig ++++ security/apparmor/match/match_default.c +@@ -0,0 +1,57 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * http://forge.novell.com/modules/xfmod/project/?apparmor ++ * ++ * AppArmor default match submodule (literal and tailglob) ++ */ ++ ++#include ++#include "match.h" ++ ++static const char *features="literal tailglob"; ++ ++void* aamatch_alloc(enum entry_match_type type) ++{ ++ return NULL; ++} ++ ++void aamatch_free(void *ptr) ++{ ++} ++ ++const char *aamatch_features(void) ++{ ++ return features; ++} ++ ++int aamatch_serialize(void *entry_extradata, struct aa_ext *e, ++ aamatch_serializecb cb) ++{ ++ return 0; ++} ++ ++unsigned int aamatch_match(const char *pathname, const char *entry_name, ++ enum entry_match_type type, void *entry_extradata) ++{ ++ int ret; ++ ++ ret = aamatch_match_common(pathname, entry_name, type); ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL_GPL(aamatch_alloc); ++EXPORT_SYMBOL_GPL(aamatch_free); ++EXPORT_SYMBOL_GPL(aamatch_features); ++EXPORT_SYMBOL_GPL(aamatch_serialize); ++EXPORT_SYMBOL_GPL(aamatch_match); ++ ++MODULE_DESCRIPTION("AppArmor match module (aamatch) [default]"); ++MODULE_AUTHOR("Tony Jones "); ++MODULE_LICENSE("GPL"); +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5008_apparmor-audit-changes.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5008_apparmor-audit-changes.patch new file mode 100644 index 0000000..917ca58 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5008_apparmor-audit-changes.patch @@ -0,0 +1,58 @@ +This patch adds AppArmor support to the audit subsystem. + +It creates id 1500 (already included in the the upstream auditd package) for +AppArmor messages. + +It also exports the audit_log_vformat function (analagous to having both +printk and vprintk exported). + +Signed-off-by: Tony Jones + +--- + include/linux/audit.h | 5 +++++ + kernel/audit.c | 3 ++- + 2 files changed, 7 insertions(+), 1 deletion(-) + +--- linux-2.6.17-rc1.orig/include/linux/audit.h ++++ linux-2.6.17-rc1/include/linux/audit.h +@@ -95,6 +95,8 @@ + #define AUDIT_LAST_KERN_ANOM_MSG 1799 + #define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ + ++#define AUDIT_AA 1500 /* AppArmor audit */ ++ + #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ + + /* Rule flags */ +@@ -349,6 +351,9 @@ + __attribute__((format(printf,4,5))); + + extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type); ++extern void audit_log_vformat(struct audit_buffer *ab, ++ const char *fmt, va_list args) ++ __attribute__((format(printf,2,0))); + extern void audit_log_format(struct audit_buffer *ab, + const char *fmt, ...) + __attribute__((format(printf,2,3))); +--- linux-2.6.17-rc1.orig/kernel/audit.c ++++ linux-2.6.17-rc1/kernel/audit.c +@@ -797,7 +797,7 @@ + * will be called a second time. Currently, we assume that a printk + * can't format message larger than 1024 bytes, so we don't either. + */ +-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, ++void audit_log_vformat(struct audit_buffer *ab, const char *fmt, + va_list args) + { + int len, avail; +@@ -999,4 +999,5 @@ + EXPORT_SYMBOL(audit_log_start); + EXPORT_SYMBOL(audit_log_end); + EXPORT_SYMBOL(audit_log_format); ++EXPORT_SYMBOL(audit_log_vformat); + EXPORT_SYMBOL(audit_log); +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5009_apparmor-add-flags.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5009_apparmor-add-flags.patch new file mode 100644 index 0000000..3a74988 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5009_apparmor-add-flags.patch @@ -0,0 +1,170 @@ +This patch adds a new function d_path_flags which takes an additional flags +parameter. Adding a new function rather than ammending the existing d_path +was done to avoid impact on the current users. + +It is not essential for inclusion with AppArmor (the apparmor_mediation.patch +can easily be revised to use plain d_path) but it enables cleaner code +["(delete)" handling] and closes a loophole with pathname generation for +chrooted tasks. + +It currently adds two flags: + +DPATH_SYSROOT: + d_path should generate a path from the system root rather than the + task's current root. + + For AppArmor this enables generation of absolute pathnames in all + cases. Currently when a task is chrooted, file access is reported + relative to the chroot. Because it is currently not possible to + obtain the absolute path in an SMP safe way, without this patch + AppArmor will have to report chroot-relative pathnames. + +DPATH_NODELETED: + d_path should not append "(deleted)" to unhashed entries. Sometimes + this information is not useful for the caller and the string can + exist as the suffix of a valid pathname. + +Signed-off-by: Tony Jones + +--- + fs/dcache.c | 48 ++++++++++++++++++++++++++++++++---------------- + include/linux/dcache.h | 7 +++++++ + 2 files changed, 39 insertions(+), 16 deletions(-) + +--- linux-2.6.17-rc1.orig/fs/dcache.c ++++ linux-2.6.17-rc1/fs/dcache.c +@@ -1381,9 +1381,11 @@ + * @rootmnt: vfsmnt to which the root dentry belongs + * @buffer: buffer to return value in + * @buflen: buffer length ++ * @flags: control flags + * + * Convert a dentry into an ASCII path name. If the entry has been deleted +- * the string " (deleted)" is appended. Note that this is ambiguous. ++ * and DPATH_NODELETED is not specified in flags then the string " (deleted)" ++ * is appended. Note that this is ambiguous. + * + * Returns the buffer or an error code if the path was too long. + * +@@ -1391,7 +1393,7 @@ + */ + static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, + struct dentry *root, struct vfsmount *rootmnt, +- char *buffer, int buflen) ++ char *buffer, int buflen, unsigned int flags) + { + char * end = buffer+buflen; + char * retval; +@@ -1399,7 +1401,8 @@ + + *--end = '\0'; + buflen--; +- if (!IS_ROOT(dentry) && d_unhashed(dentry)) { ++ if (!(flags & DPATH_NODELETED) && ++ !IS_ROOT(dentry) && d_unhashed(dentry)) { + buflen -= 10; + end -= 10; + if (buflen < 0) +@@ -1416,7 +1419,8 @@ + for (;;) { + struct dentry * parent; + +- if (dentry == root && vfsmnt == rootmnt) ++ if (!(flags & DPATH_SYSROOT) && ++ dentry == root && vfsmnt == rootmnt) + break; + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + /* Global root? */ +@@ -1458,25 +1462,36 @@ + } + + /* write full pathname into buffer and return start of pathname */ +-char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt, +- char *buf, int buflen) ++char * d_path_flags(struct dentry *dentry, struct vfsmount *vfsmnt, ++ char *buf, int buflen, unsigned int flags) + { + char *res; +- struct vfsmount *rootmnt; +- struct dentry *root; ++ struct vfsmount *rootmnt = NULL; ++ struct dentry *root = NULL; + +- read_lock(¤t->fs->lock); +- rootmnt = mntget(current->fs->rootmnt); +- root = dget(current->fs->root); +- read_unlock(¤t->fs->lock); ++ if (!(flags & DPATH_SYSROOT)){ ++ read_lock(¤t->fs->lock); ++ rootmnt = mntget(current->fs->rootmnt); ++ root = dget(current->fs->root); ++ read_unlock(¤t->fs->lock); ++ } + spin_lock(&dcache_lock); +- res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen); ++ res = __d_path(dentry, vfsmnt, root, rootmnt, buf, buflen, flags); + spin_unlock(&dcache_lock); +- dput(root); +- mntput(rootmnt); ++ if (!(flags & DPATH_SYSROOT)){ ++ dput(root); ++ mntput(rootmnt); ++ } + return res; + } + ++/* original d_path without support for flags */ ++char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt, ++ char *buf, int buflen) ++{ ++ return d_path_flags(dentry, vfsmnt, buf, buflen, 0); ++} ++ + /* + * NOTE! The user-level library version returns a + * character pointer. The kernel system call just +@@ -1519,7 +1534,7 @@ + unsigned long len; + char * cwd; + +- cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE); ++ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE, 0); + spin_unlock(&dcache_lock); + + error = PTR_ERR(cwd); +@@ -1771,6 +1786,7 @@ + EXPORT_SYMBOL(d_invalidate); + EXPORT_SYMBOL(d_lookup); + EXPORT_SYMBOL(d_move); ++EXPORT_SYMBOL(d_path_flags); + EXPORT_SYMBOL(d_path); + EXPORT_SYMBOL(d_prune_aliases); + EXPORT_SYMBOL(d_rehash); +--- linux-2.6.17-rc1.orig/include/linux/dcache.h ++++ linux-2.6.17-rc1/include/linux/dcache.h +@@ -164,6 +164,10 @@ + + #define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */ + ++/* dpath flags */ ++#define DPATH_SYSROOT 0x0001 /* continue past fsroot (chroot) */ ++#define DPATH_NODELETED 0x0002 /* do not append " (deleted)" */ ++ + extern spinlock_t dcache_lock; + + /** +@@ -281,6 +285,9 @@ + extern int d_validate(struct dentry *, struct dentry *); + + extern char * d_path(struct dentry *, struct vfsmount *, char *, int); ++ ++extern char * d_path_flags(struct dentry *, struct vfsmount *, char *, int, ++ unsigned int); + + /* Allocation counts.. */ + +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5010_apparmor-export-namespace-semaphor.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5010_apparmor-export-namespace-semaphor.patch new file mode 100644 index 0000000..8504f75 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/5010_apparmor-export-namespace-semaphor.patch @@ -0,0 +1,53 @@ +This patch exports the namespace_sem semaphore. + +The shared subtree patches which went into 2.6.15-rc1 replaced the old +namespace semaphore which used to be per namespace (and visible) with a +new single static semaphore. + +The reason for this change is that currently visibility of vfsmount information +to the LSM hooks is fairly patchy. Either there is no passed parameter or +it can be NULL. For the case of the former, several LSM hooks that we +require to mediate have no vfsmount/nameidata passed. We previously (mis)used +the visibility of the old per namespace semaphore to walk the processes +namespace looking for vfsmounts with a root dentry matching the dentry we were +trying to mediate. + +Clearly this is not viable long term strategy and changes working towards +passing a vfsmount to all relevant LSM hooks would seem necessary (and also +useful for other users of LSM). Alternative suggestions and ideas are welcomed. + +Signed-off-by: Tony Jones + +--- + fs/namespace.c | 3 ++- + include/linux/namespace.h | 2 ++ + 2 files changed, 4 insertions(+), 1 deletion(-) + +--- linux-2.6.17-rc1.orig/fs/namespace.c ++++ linux-2.6.17-rc1/fs/namespace.c +@@ -46,7 +46,8 @@ + static struct list_head *mount_hashtable __read_mostly; + static int hash_mask __read_mostly, hash_bits __read_mostly; + static kmem_cache_t *mnt_cache __read_mostly; +-static struct rw_semaphore namespace_sem; ++struct rw_semaphore namespace_sem; ++EXPORT_SYMBOL_GPL(namespace_sem); + + /* /sys/fs */ + decl_subsys(fs, NULL, NULL); +--- linux-2.6.17-rc1.orig/include/linux/namespace.h ++++ linux-2.6.17-rc1/include/linux/namespace.h +@@ -5,6 +5,8 @@ + #include + #include + ++extern struct rw_semaphore namespace_sem; ++ + struct namespace { + atomic_t count; + struct vfsmount * root; +- +To unsubscribe from this list: send the line "unsubscribe linux-kernel" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html +Please read the FAQ at http://www.tux.org/lkml/ \ No newline at end of file diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_audit.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_audit.patch new file mode 100644 index 0000000..89c65e2 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_audit.patch @@ -0,0 +1,59 @@ +Subject: Export audit subsystem for use by modules +Patch-mainline: no + +Adds necessary export symbols for audit subsystem routines. +Changes audit_log_vformat to be externally visible (analagous to vprintf) +Patch is not in mainline -- pending AppArmor code submission to lkml + +Index: linux-2.6.17/include/linux/audit.h +=================================================================== +--- linux-2.6.17.orig/include/linux/audit.h ++++ linux-2.6.17/include/linux/audit.h +@@ -92,6 +92,8 @@ + #define AUDIT_MAC_STATUS 1404 /* Changed enforcing,permissive,off */ + #define AUDIT_MAC_CONFIG_CHANGE 1405 /* Changes to booleans */ + ++#define AUDIT_SD 1500 /* AppArmor (SubDomain) audit */ ++ + #define AUDIT_FIRST_KERN_ANOM_MSG 1700 + #define AUDIT_LAST_KERN_ANOM_MSG 1799 + #define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */ +@@ -357,6 +359,9 @@ + __attribute__((format(printf,4,5))); + + extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type); ++extern void audit_log_vformat(struct audit_buffer *ab, ++ const char *fmt, va_list args) ++ __attribute__((format(printf,2,0))); + extern void audit_log_format(struct audit_buffer *ab, + const char *fmt, ...) + __attribute__((format(printf,2,3))); + +Index: linux-2.6.17/kernel/audit.c +=================================================================== +--- linux-2.6.17.orig/kernel/audit.c ++++ linux-2.6.17/kernel/audit.c +@@ -893,7 +893,7 @@ + * will be called a second time. Currently, we assume that a printk + * can't format message larger than 1024 bytes, so we don't either. + */ +-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, ++void audit_log_vformat(struct audit_buffer *ab, const char *fmt, + va_list args) + { + int len, avail; +@@ -1092,7 +1092,10 @@ + } + } + +-EXPORT_SYMBOL(audit_log_start); +-EXPORT_SYMBOL(audit_log_end); +-EXPORT_SYMBOL(audit_log_format); +-EXPORT_SYMBOL(audit_log); ++EXPORT_SYMBOL_GPL(audit_log_start); ++EXPORT_SYMBOL_GPL(audit_log_vformat); ++EXPORT_SYMBOL_GPL(audit_log_format); ++EXPORT_SYMBOL_GPL(audit_log_untrustedstring); ++EXPORT_SYMBOL_GPL(audit_log_d_path); ++EXPORT_SYMBOL_GPL(audit_log_end); ++EXPORT_SYMBOL_GPL(audit_log); diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_main.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_main.patch new file mode 100644 index 0000000..faba686 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_main.patch @@ -0,0 +1,7917 @@ +From: tonyj@suse.de +Subject: AppArmor (SubDomain) +Patch-mainline: no + +AppArmor security module (previously called SubDomain) +Patch is not in mainline -- pending code submission to lkml + + +--- linux-2.6.16-SL101_BRANCH.orig/security/Kconfig ++++ linux-2.6.16-SL101_BRANCH/security/Kconfig +@@ -100,6 +100,7 @@ + If you are unsure how to answer this question, answer N. + + source security/selinux/Kconfig ++source security/apparmor/Kconfig + + endmenu + +--- linux-2.6.16-SL101_BRANCH.orig/security/Makefile ++++ linux-2.6.16-SL101_BRANCH/security/Makefile +@@ -4,6 +4,7 @@ + + obj-$(CONFIG_KEYS) += keys/ + subdir-$(CONFIG_SECURITY_SELINUX) += selinux ++subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor + + # We always need commoncap as it's default + obj-y += commoncap.o +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/Makefile +@@ -0,0 +1,10 @@ ++# Makefile for AppArmor Linux Security Module (previously called "SubDomain") ++# ++APPARMOR_VER=2.0-19.43r6320 ++ ++EXTRA_CFLAGS += -DAPPARMOR_VERSION=\"${APPARMOR_VER}\" ++ ++subdir-$(CONFIG_SECURITY_APPARMOR) += aamatch ++obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o ++ ++apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o capabilities.o module_interface.o apparmor_version.o +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/capabilities.c +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor capability definitions ++ */ ++ ++#include "apparmor.h" ++ ++static const char *capnames[] = { ++ "chown", ++ "dac_override", ++ "dac_read_search", ++ "fowner", ++ "fsetid", ++ "kill", ++ "setgid", ++ "setuid", ++ "setpcap", ++ "linux_immutable", ++ "net_bind_service", ++ "net_broadcast", ++ "net_admin", ++ "net_raw", ++ "ipc_lock", ++ "ipc_owner", ++ "sys_module", ++ "sys_rawio", ++ "sys_chroot", ++ "sys_ptrace", ++ "sys_pacct", ++ "sys_admin", ++ "sys_boot", ++ "sys_nice", ++ "sys_resource", ++ "sys_time", ++ "sys_tty_config", ++ "mknod", ++ "lease" ++}; ++ ++const char *capability_to_name(unsigned int cap) ++{ ++ const char *capname; ++ ++ capname = (cap < (sizeof(capnames) / sizeof(char *)) ++ ? capnames[cap] : "invalid-capability"); ++ ++ return capname; ++} +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/inline.h +@@ -0,0 +1,362 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef __INLINE_H ++#define __INLINE_H ++ ++#include ++ ++static inline int __sd_is_confined(struct subdomain *sd) ++{ ++ int rc = 0; ++ ++ if (sd && sd->sd_magic == SD_ID_MAGIC && sd->profile) { ++ BUG_ON(!sd->active); ++ rc = 1; ++ } ++ ++ return rc; ++} ++ ++/** ++ * sd_is_confined ++ * @sd: subdomain ++ * ++ * Check if @sd is confined (contains a valid profile) ++ * Return 1 if confined, 0 otherwise. ++ */ ++static inline int sd_is_confined(void) ++{ ++ struct subdomain *sd = SD_SUBDOMAIN(current->security); ++ return __sd_is_confined(sd); ++} ++ ++static inline int __sd_sub_defined(struct subdomain *sd) ++{ ++ return __sd_is_confined(sd) && !list_empty(&sd->profile->sub); ++} ++ ++/** ++ * sd_sub_defined ++ * @sd: subdomain ++ * ++ * Check if @sd has at least one subprofile ++ * Return 1 if true, 0 otherwise ++ */ ++static inline int sd_sub_defined(void) ++{ ++ struct subdomain *sd = SD_SUBDOMAIN(current->security); ++ return __sd_sub_defined(sd); ++} ++ ++/** ++ * get_sdprofile ++ * @p: profile ++ * ++ * Increment refcount on profile ++ */ ++static inline struct sdprofile *get_sdprofile(struct sdprofile *p) ++{ ++ if (p) ++ atomic_inc(&p->count); ++ return p; ++} ++ ++/** ++ * put_sdprofile ++ * @p: profile ++ * ++ * Decrement refcount on profile ++ */ ++static inline void put_sdprofile(struct sdprofile *p) ++{ ++ if (p) ++ if (atomic_dec_and_test(&p->count)) ++ free_sdprofile(p); ++} ++ ++/** ++ * sd_switch ++ * @sd: subdomain to switch ++ * @profile: new profile ++ * @active: new active ++ * ++ * Change subdomain to use new profiles. ++ */ ++static inline void sd_switch(struct subdomain *sd, ++ struct sdprofile *profile, ++ struct sdprofile *active) ++{ ++ /* noop if NULL */ ++ put_sdprofile(sd->profile); ++ put_sdprofile(sd->active); ++ ++ sd->profile = get_sdprofile(profile); ++ sd->active = get_sdprofile(active); ++} ++ ++/** ++ * sd_switch_unconfined ++ * @sd: subdomain to switch ++ * ++ * Change subdomain to unconfined ++ */ ++static inline void sd_switch_unconfined(struct subdomain *sd) ++{ ++ sd_switch(sd, NULL, NULL); ++ ++ /* reset magic in case we were in a subhat before */ ++ sd->sd_hat_magic = 0; ++} ++ ++/** ++ * alloc_subdomain ++ * @tsk: task struct ++ * ++ * Allocate a new subdomain including a backpointer to it's referring task. ++ */ ++static inline struct subdomain *alloc_subdomain(struct task_struct *tsk) ++{ ++ struct subdomain *sd; ++ ++ sd = kmalloc(sizeof(struct subdomain), GFP_KERNEL); ++ if (!sd) ++ goto out; ++ ++ /* zero it first */ ++ memset(sd, 0, sizeof(struct subdomain)); ++ sd->sd_magic = SD_ID_MAGIC; ++ ++ /* back pointer to task */ ++ sd->task = tsk; ++ ++ /* any readers of the list must make sure that they can handle ++ * case where sd->profile and sd->active are not yet set (null) ++ */ ++ sd_subdomainlist_add(sd); ++ ++out: ++ return sd; ++} ++ ++/** ++ * free_subdomain ++ * @sd: subdomain ++ * ++ * Free a subdomain previously allocated by alloc_subdomain ++ */ ++static inline void free_subdomain(struct subdomain *sd) ++{ ++ sd_subdomainlist_remove(sd); ++ kfree(sd); ++} ++ ++/** ++ * alloc_sdprofile ++ * ++ * Allocate, initialize and return a new zeroed profile. ++ * Returns NULL on failure. ++ */ ++static inline struct sdprofile *alloc_sdprofile(void) ++{ ++ struct sdprofile *profile; ++ ++ profile = (struct sdprofile *)kmalloc(sizeof(struct sdprofile), ++ GFP_KERNEL); ++ SD_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ if (profile) { ++ int i; ++ memset(profile, 0, sizeof(struct sdprofile)); ++ INIT_LIST_HEAD(&profile->list); ++ INIT_LIST_HEAD(&profile->sub); ++ INIT_LIST_HEAD(&profile->file_entry); ++ for (i = 0; i <= POS_SD_FILE_MAX; i++) { ++ INIT_LIST_HEAD(&profile->file_entryp[i]); ++ } ++ } ++ return profile; ++} ++ ++/** ++ * sd_put_name ++ * @name: name to release. ++ * ++ * Release space (free_page) allocated to hold pathname ++ * name may be NULL (checked for by free_page) ++ */ ++static inline void sd_put_name(const char *name) ++{ ++ free_page((unsigned long)name); ++} ++ ++/** __sd_find_profile ++ * @name: name of profile to find ++ * @head: list to search ++ * ++ * Return reference counted copy of profile. NULL if not found ++ * Caller must hold any necessary locks ++ */ ++static inline struct sdprofile *__sd_find_profile(const char *name, ++ struct list_head *head) ++{ ++ struct sdprofile *p; ++ ++ if (!name || !head) ++ return NULL; ++ ++ SD_DEBUG("%s: finding profile %s\n", __FUNCTION__, name); ++ list_for_each_entry(p, head, list) { ++ if (!strcmp(p->name, name)) { ++ /* return refcounted object */ ++ p = get_sdprofile(p); ++ return p; ++ } else { ++ SD_DEBUG("%s: skipping %s\n", __FUNCTION__, p->name); ++ } ++ } ++ return NULL; ++} ++ ++static inline struct subdomain *__get_sdcopy(struct subdomain *new, ++ struct task_struct *tsk) ++{ ++ struct subdomain *old, *temp = NULL; ++ ++ old = SD_SUBDOMAIN(tsk->security); ++ ++ if (old) { ++ new->sd_magic = old->sd_magic; ++ new->sd_hat_magic = old->sd_hat_magic; ++ ++ new->active = get_sdprofile(old->active); ++ ++ if (old->profile == old->active) ++ new->profile = new->active; ++ else ++ new->profile = get_sdprofile(old->profile); ++ ++ temp = new; ++ } ++ ++ return temp; ++} ++ ++/** get_sdcopy ++ * @new: subdomain to hold copy ++ * ++ * Make copy of current subdomain containing refcounted profile and active ++ * Used to protect readers against racing writers (changehat and profile ++ * replacement). ++ */ ++static inline struct subdomain *get_sdcopy(struct subdomain *new) ++{ ++ struct subdomain *temp; ++ unsigned long flags; ++ ++ read_lock_irqsave(&sd_lock, flags); ++ ++ temp = __get_sdcopy(new, current); ++ ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ return temp; ++} ++ ++/** get_sdcopy ++ * @temp: subdomain to drop refcounts on ++ * ++ * Drop refcounted profile/active in copy of subdomain made by get_sdcopy ++ */ ++static inline void put_sdcopy(struct subdomain *temp) ++{ ++ if (temp) { ++ put_sdprofile(temp->active); ++ if (temp->active != temp->profile) ++ (void)put_sdprofile(temp->profile); ++ } ++} ++ ++/** sd_path_begin2 ++ * @rdentry: filesystem root dentry (searching for vfsmnts matching this) ++ * @dentry: dentry object to obtain pathname from (relative to matched vfsmnt) ++ * ++ * Setup data for iterating over vfsmounts (in current tasks namespace). ++ */ ++static inline void sd_path_begin2(struct dentry *rdentry, ++ struct dentry *dentry, ++ struct sd_path_data *data) ++{ ++ data->dentry = dentry; ++ data->root = dget(rdentry->d_sb->s_root); ++ data->namespace = current->namespace; ++ data->head = &data->namespace->list; ++ data->pos = data->head->next; ++ prefetch(data->pos->next); ++ data->errno = 0; ++ ++ down_read(&namespace_sem); ++} ++ ++/** sd_path_begin ++ * @dentry filesystem root dentry and object to obtain pathname from ++ * ++ * Utility function for calling _sd_path_begin for when the dentry we are ++ * looking for and the root are the same (this is the usual case). ++ */ ++static inline void sd_path_begin(struct dentry *dentry, ++ struct sd_path_data *data) ++{ ++ sd_path_begin2(dentry, dentry, data); ++} ++ ++/** sd_path_end ++ * @data: data object previously initialized by sd_path_begin ++ * ++ * End iterating over vfsmounts. ++ * If an error occured in begin or get, it is returned. Otherwise 0. ++ */ ++static inline int sd_path_end(struct sd_path_data *data) ++{ ++ up_read(&namespace_sem); ++ dput(data->root); ++ ++ return data->errno; ++} ++ ++/** sd_path_getname ++ * @data: data object previously initialized by sd_path_begin ++ * ++ * Return the next mountpoint which has the same root dentry as data->root. ++ * If no more mount points exist (or in case of error) NULL is returned ++ * (caller should call sd_path_end() and inspect return code to differentiate) ++ */ ++static inline char *sd_path_getname(struct sd_path_data *data) ++{ ++ char *name = NULL; ++ struct vfsmount *mnt; ++ ++ while (data->pos != data->head) { ++ mnt = list_entry(data->pos, struct vfsmount, mnt_list); ++ ++ /* advance to next -- so that it is done before we break */ ++ data->pos = data->pos->next; ++ prefetch(data->pos->next); ++ ++ if (mnt->mnt_root == data->root) { ++ name = sd_get_name(data->dentry, mnt); ++ if (!name) ++ data->errno = -ENOMEM; ++ break; ++ } ++ } ++ ++ return name; ++} ++ ++#endif /* __INLINE_H__ */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/list.c +@@ -0,0 +1,289 @@ ++/* ++ * Copyright (C) 1998-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor Profile List Management ++ */ ++ ++#include ++#include "apparmor.h" ++#include "inline.h" ++ ++/* list of all profiles and lock */ ++static LIST_HEAD(profile_list); ++static rwlock_t profile_lock = RW_LOCK_UNLOCKED; ++ ++/* list of all subdomains and lock */ ++static LIST_HEAD(subdomain_list); ++static rwlock_t subdomain_lock = RW_LOCK_UNLOCKED; ++ ++/** ++ * sd_profilelist_find ++ * @name: profile name (program name) ++ * ++ * Search the profile list for profile @name. Return refcounted profile on ++ * success, NULL on failure. ++ */ ++struct sdprofile *sd_profilelist_find(const char *name) ++{ ++ struct sdprofile *p = NULL; ++ if (name) { ++ read_lock(&profile_lock); ++ p = __sd_find_profile(name, &profile_list); ++ read_unlock(&profile_lock); ++ } ++ return p; ++} ++ ++/** ++ * sd_profilelist_add ++ * @profile: new profile to add to list ++ * ++ * Add new profile to list. Reference count on profile is incremented. ++ * Return 1 on success, 0 on failure (bad profile or already exists) ++ */ ++int sd_profilelist_add(struct sdprofile *profile) ++{ ++ struct sdprofile *old_profile; ++ int ret = 0; ++ ++ if (!profile) ++ goto out; ++ ++ write_lock(&profile_lock); ++ old_profile = __sd_find_profile(profile->name, &profile_list); ++ if (old_profile) { ++ put_sdprofile(old_profile); ++ goto out; ++ } ++ profile = get_sdprofile(profile); ++ list_add(&profile->list, &profile_list); ++ ret = 1; ++ out: ++ write_unlock(&profile_lock); ++ return ret; ++} ++ ++/** ++ * sd_profilelist_remove ++ * @name: name of profile to be removed ++ * ++ * Remove profile from list. Reference count on profile is decremented. ++ */ ++int sd_profilelist_remove(const char *name) ++{ ++ struct sdprofile *profile = NULL; ++ struct sdprofile *p, *tmp; ++ int error = -ENOENT; ++ ++ if (!name) ++ goto out; ++ ++ write_lock(&profile_lock); ++ list_for_each_entry_safe(p, tmp, &profile_list, list) { ++ if (!strcmp(p->name, name)) { ++ list_del_init(&p->list); ++ profile = p; ++ break; ++ } ++ } ++ write_unlock(&profile_lock); ++ if (!profile) ++ goto out; ++ ++ put_sdprofile(profile); ++ error = 0; ++out: ++ return error; ++} ++ ++/** ++ * sd_profilelist_replace ++ * @profile - new profile ++ * ++ * Replace a profile on the profile list. Find the old profile by name in ++ * the list, and replace it with the new profile. This is an atomic ++ * list operation. Returns the old profile (which is still refcounted) if ++ * there was one, or NULL. ++ */ ++struct sdprofile *sd_profilelist_replace(struct sdprofile *profile) ++{ ++ struct sdprofile *oldprofile; ++ ++ write_lock(&profile_lock); ++ oldprofile = __sd_find_profile(profile->name, &profile_list); ++ if (oldprofile) { ++ list_del_init(&oldprofile->list); ++ ++ /* __sd_find_profile incremented count, so adjust down */ ++ put_sdprofile(oldprofile); ++ } ++ profile = get_sdprofile(profile); ++ list_add(&profile->list, &profile_list); ++ write_unlock(&profile_lock); ++ ++ return oldprofile; ++} ++ ++/** ++ * sd_profilelist_release ++ * ++ * Remove all profiles from profile_list ++ */ ++void sd_profilelist_release(void) ++{ ++ struct sdprofile *p, *tmp; ++ ++ write_lock(&profile_lock); ++ list_for_each_entry_safe(p, tmp, &profile_list, list) { ++ list_del_init(&p->list); ++ put_sdprofile(p); ++ } ++ write_unlock(&profile_lock); ++} ++ ++/** ++ * sd_subdomainlist_add ++ * @sd: new subdomain ++ * ++ * Add subdomain to subdomain_list ++ */ ++void sd_subdomainlist_add(struct subdomain *sd) ++{ ++ unsigned long flags; ++ ++ if (!sd) { ++ SD_INFO("%s: bad subdomain\n", __FUNCTION__); ++ return; ++ } ++ ++ write_lock_irqsave(&subdomain_lock, flags); ++ list_add(&sd->list, &subdomain_list); ++ write_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/** ++ * sd_subdomainlist_remove ++ * @sd: subdomain to be removed ++ * ++ * Remove subdomain from subdomain_list ++ */ ++void sd_subdomainlist_remove(struct subdomain *sd) ++{ ++ unsigned long flags; ++ ++ if (sd) { ++ write_lock_irqsave(&subdomain_lock, flags); ++ list_del_init(&sd->list); ++ write_unlock_irqrestore(&subdomain_lock, flags); ++ } ++} ++ ++/** ++ * sd_subdomainlist_iterate ++ * @func: method to be called for each element ++ * @cookie: user passed data ++ * ++ * Iterate over subdomain list, stop when sd_iter func returns non zero ++ */ ++void sd_subdomainlist_iterate(sd_iter func, void *cookie) ++{ ++ struct subdomain *node; ++ int ret = 0; ++ unsigned long flags; ++ ++ read_lock_irqsave(&subdomain_lock, flags); ++ list_for_each_entry(node, &subdomain_list, list) { ++ ret = (*func) (node, cookie); ++ if (ret != 0) ++ break; ++ } ++ read_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/** ++ * sd_subdomainlist_iterateremove ++ * @func: method to be called for each element ++ * @cookie: user passed data ++ * ++ * Iterate over subdomain_list, remove element when sd_iter func returns ++ * non zero ++ */ ++void sd_subdomainlist_iterateremove(sd_iter func, void *cookie) ++{ ++ struct subdomain *node, *tmp; ++ int ret = 0; ++ unsigned long flags; ++ ++ write_lock_irqsave(&subdomain_lock, flags); ++ list_for_each_entry_safe(node, tmp, &subdomain_list, list) { ++ ret = (*func) (node, cookie); ++ if (ret != 0) ++ list_del_init(&node->list); ++ } ++ write_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/** ++ * sd_subdomainlist_release ++ * ++ * Remove all subdomains from subdomain_list ++ */ ++void sd_subdomainlist_release() ++{ ++ struct subdomain *node, *tmp; ++ unsigned long flags; ++ ++ write_lock_irqsave(&subdomain_lock, flags); ++ list_for_each_entry_safe(node, tmp, &subdomain_list, list) { ++ list_del_init(&node->list); ++ } ++ write_unlock_irqrestore(&subdomain_lock, flags); ++} ++ ++/* seq_file helper routines ++ * Used by subdomainfs.c to iterate over profile_list ++ */ ++static void *p_start(struct seq_file *f, loff_t *pos) ++{ ++ struct sdprofile *node; ++ loff_t l = *pos; ++ ++ read_lock(&profile_lock); ++ list_for_each_entry(node, &profile_list, list) ++ if (!l--) ++ return node; ++ return NULL; ++} ++ ++static void *p_next(struct seq_file *f, void *p, loff_t *pos) ++{ ++ struct list_head *lh = ((struct sdprofile *)p)->list.next; ++ (*pos)++; ++ return lh == &profile_list ? ++ NULL : list_entry(lh, struct sdprofile, list); ++} ++ ++static void p_stop(struct seq_file *f, void *v) ++{ ++ read_unlock(&profile_lock); ++} ++ ++static int seq_show_profile(struct seq_file *f, void *v) ++{ ++ struct sdprofile *profile = (struct sdprofile *)v; ++ seq_printf(f, "%s (%s)\n", profile->name, ++ PROFILE_COMPLAIN(profile) ? "complain" : "enforce"); ++ return 0; ++} ++ ++struct seq_operations subdomainfs_profiles_op = { ++ .start = p_start, ++ .next = p_next, ++ .stop = p_stop, ++ .show = seq_show_profile, ++}; +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/lsm.c +@@ -0,0 +1,858 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * http://forge.novell.com/modules/xfmod/project/?apparmor ++ * ++ * Immunix AppArmor LSM interface (previously called "SubDomain") ++ */ ++ ++#include ++#include ++ ++/* superblock types */ ++ ++/* PIPEFS_MAGIC */ ++#include ++/* from net/socket.c */ ++#define SOCKFS_MAGIC 0x534F434B ++/* from inotify.c */ ++#define INOTIFYFS_MAGIC 0xBAD1DEA ++ ++#define VALID_FSTYPE(inode) ((inode)->i_sb->s_magic != PIPEFS_MAGIC && \ ++ (inode)->i_sb->s_magic != SOCKFS_MAGIC && \ ++ (inode)->i_sb->s_magic != INOTIFYFS_MAGIC) ++ ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++/* main SD lock [see get_sdcopy and put_sdcopy] */ ++rwlock_t sd_lock = RW_LOCK_UNLOCKED; ++ ++/* Flag values, also controllable via subdomainfs/control. ++ * We explicitly do not allow these to be modifiable when exported via ++ * /sys/modules/parameters, as we want to do additional mediation and ++ * don't want to add special path code. */ ++ ++/* Complain mode (used to be 'bitch' mode) */ ++int subdomain_complain = 0; ++module_param_named(complain, subdomain_complain, int, S_IRUSR); ++MODULE_PARM_DESC(subdomain_complain, "Toggle AppArmor complain mode"); ++ ++/* Debug mode */ ++int subdomain_debug = 0; ++module_param_named(debug, subdomain_debug, int, S_IRUSR); ++MODULE_PARM_DESC(subdomain_debug, "Toggle AppArmor debug mode"); ++ ++/* Audit mode */ ++int subdomain_audit = 0; ++module_param_named(audit, subdomain_audit, int, S_IRUSR); ++MODULE_PARM_DESC(subdomain_audit, "Toggle AppArmor audit mode"); ++ ++/* Syscall logging mode */ ++int subdomain_logsyscall = 0; ++module_param_named(logsyscall, subdomain_logsyscall, int, S_IRUSR); ++MODULE_PARM_DESC(subdomain_logsyscall, "Toggle AppArmor logsyscall mode"); ++ ++#ifndef MODULE ++static int __init sd_getopt_complain(char *str) ++{ ++ get_option(&str, &subdomain_complain); ++ return 1; ++} ++__setup("subdomain_complain=", sd_getopt_complain); ++ ++static int __init sd_getopt_debug(char *str) ++{ ++ get_option(&str, &subdomain_debug); ++ return 1; ++} ++__setup("subdomain_debug=", sd_getopt_debug); ++ ++static int __init sd_getopt_audit(char *str) ++{ ++ get_option(&str, &subdomain_audit); ++ return 1; ++} ++__setup("subdomain_audit=", sd_getopt_audit); ++ ++static int __init sd_getopt_logsyscall(char *str) ++{ ++ get_option(&str, &subdomain_logsyscall); ++ return 1; ++} ++__setup("subdomain_logsyscall=", sd_getopt_logsyscall); ++#endif ++ ++static int subdomain_ptrace(struct task_struct *parent, ++ struct task_struct *child) ++{ ++ int error; ++ struct subdomain *sd; ++ unsigned long flags; ++ ++ error = cap_ptrace(parent, child); ++ ++ read_lock_irqsave(&sd_lock, flags); ++ ++ sd = SD_SUBDOMAIN(current->security); ++ ++ if (!error && __sd_is_confined(sd)) { ++ error = sd_audit_syscallreject(sd, GFP_ATOMIC, "ptrace"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ return error; ++} ++ ++static int subdomain_capget(struct task_struct *target, ++ kernel_cap_t * effective, ++ kernel_cap_t * inheritable, ++ kernel_cap_t * permitted) ++{ ++ return cap_capget(target, effective, inheritable, permitted); ++} ++ ++static int subdomain_capset_check(struct task_struct *target, ++ kernel_cap_t *effective, ++ kernel_cap_t *inheritable, ++ kernel_cap_t *permitted) ++{ ++ return cap_capset_check(target, effective, inheritable, permitted); ++} ++ ++static void subdomain_capset_set(struct task_struct *target, ++ kernel_cap_t *effective, ++ kernel_cap_t *inheritable, ++ kernel_cap_t *permitted) ++{ ++ cap_capset_set(target, effective, inheritable, permitted); ++ return; ++} ++ ++static int subdomain_capable(struct task_struct *tsk, int cap) ++{ ++ int error; ++ ++ /* cap_capable returns 0 on success, else -EPERM */ ++ error = cap_capable(tsk, cap); ++ ++ if (error == 0) { ++ struct subdomain *sd, sdcopy; ++ unsigned long flags; ++ ++ read_lock_irqsave(&sd_lock, flags); ++ sd = __get_sdcopy(&sdcopy, tsk); ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ error = sd_capability(sd, cap); ++ ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++ ++static int subdomain_sysctl(struct ctl_table *table, int op) ++{ ++ int error = 0; ++ struct subdomain *sd; ++ unsigned long flags; ++ ++ read_lock_irqsave(&sd_lock, flags); ++ ++ sd = SD_SUBDOMAIN(current->security); ++ ++ if ((op & 002) && __sd_is_confined(sd) && !capable(CAP_SYS_ADMIN)) { ++ error = sd_audit_syscallreject(sd, GFP_ATOMIC, ++ "sysctl (write)"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ return error; ++} ++ ++static int subdomain_syslog(int type) ++{ ++ return cap_syslog(type); ++} ++ ++static int subdomain_netlink_send(struct sock *sk, struct sk_buff *skb) ++{ ++ return cap_netlink_send(sk, skb); ++} ++ ++static int subdomain_netlink_recv(struct sk_buff *skb) ++{ ++ return cap_netlink_recv(skb); ++} ++ ++static void subdomain_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) ++{ ++ cap_bprm_apply_creds(bprm, unsafe); ++ return; ++} ++ ++static int subdomain_bprm_set_security(struct linux_binprm *bprm) ++{ ++ /* handle capability bits with setuid, etc */ ++ cap_bprm_set_security(bprm); ++ /* already set based on script name */ ++ if (bprm->sh_bang) ++ return 0; ++ return sd_register(bprm->file); ++} ++ ++static int subdomain_sb_mount(char *dev_name, struct nameidata *nd, char *type, ++ unsigned long flags, void *data) ++{ ++ int error = 0; ++ struct subdomain *sd; ++ unsigned long lockflags; ++ ++ read_lock_irqsave(&sd_lock, lockflags); ++ ++ sd = SD_SUBDOMAIN(current->security); ++ ++ if (__sd_is_confined(sd)) { ++ error = sd_audit_syscallreject(sd, GFP_ATOMIC, "mount"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ read_unlock_irqrestore(&sd_lock, lockflags); ++ ++ return error; ++} ++ ++static int subdomain_umount(struct vfsmount *mnt, int flags) ++{ ++ int error = 0; ++ struct subdomain *sd; ++ unsigned long lockflags; ++ ++ read_lock_irqsave(&sd_lock, lockflags); ++ ++ sd = SD_SUBDOMAIN(current->security); ++ ++ if (__sd_is_confined(sd)) { ++ error = sd_audit_syscallreject(sd, GFP_KERNEL, "umount"); ++ WARN_ON(error != -EPERM); ++ } ++ ++ read_unlock_irqrestore(&sd_lock, lockflags); ++ ++ return error; ++} ++ ++static int subdomain_inode_mkdir(struct inode *inode, struct dentry *dentry, ++ int mask) ++{ ++ struct subdomain sdcopy, *sd; ++ int error; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ error = sd_perm_dir(sd, dentry, SD_DIR_MKDIR); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_rmdir(struct inode *inode, struct dentry *dentry) ++{ ++ struct subdomain sdcopy, *sd; ++ int error; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ error = sd_perm_dir(sd, dentry, SD_DIR_RMDIR); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_create(struct inode *inode, struct dentry *dentry, ++ int mask) ++{ ++ struct subdomain sdcopy, *sd; ++ int error; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ /* At a minimum, need write perm to create */ ++ error = sd_perm_dentry(sd, dentry, MAY_WRITE); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_link(struct dentry *old_dentry, struct inode *inode, ++ struct dentry *new_dentry) ++{ ++ int error = 0; ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ error = sd_link(sd, new_dentry, old_dentry); ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_unlink(struct inode *inode, struct dentry *dentry) ++{ ++ struct subdomain sdcopy, *sd; ++ int error; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ error = sd_perm_dentry(sd, dentry, MAY_WRITE); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_mknod(struct inode *inode, struct dentry *dentry, ++ int mode, dev_t dev) ++{ ++ struct subdomain sdcopy, *sd; ++ int error = 0; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ error = sd_perm_dentry(sd, dentry, MAY_WRITE); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_rename(struct inode *old_inode, ++ struct dentry *old_dentry, ++ struct inode *new_inode, ++ struct dentry *new_dentry) ++{ ++ struct subdomain sdcopy, *sd; ++ int error = 0; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ error = sd_perm_dentry(sd, old_dentry, ++ MAY_READ | MAY_WRITE); ++ ++ if (!error) ++ error = sd_perm_dentry(sd, new_dentry, MAY_WRITE); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_inode_permission(struct inode *inode, int mask, ++ struct nameidata *nd) ++{ ++ int error = 0; ++ ++ /* Do not perform check on pipes or sockets ++ * Same as subdomain_file_permission ++ */ ++ if (VALID_FSTYPE(inode)) { ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ error = sd_perm_nameidata(sd, nd, mask); ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++ ++static int subdomain_inode_setattr(struct dentry *dentry, struct iattr *iattr) ++{ ++ struct subdomain sdcopy, *sd; ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ /* ++ * Mediate any attempt to change attributes of a file ++ * (chmod, chown, chgrp, etc) ++ */ ++ error = sd_attr(sd, dentry, iattr); ++ ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++ ++static int subdomain_inode_setxattr(struct dentry *dentry, char *name, ++ void *value, size_t size, int flags) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ error = sd_xattr(sd, dentry, name, SD_XATTR_SET); ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++ ++static int subdomain_inode_getxattr(struct dentry *dentry, char *name) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ error = sd_xattr(sd, dentry, name, SD_XATTR_GET); ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++static int subdomain_inode_listxattr(struct dentry *dentry) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ error = sd_xattr(sd, dentry, NULL, SD_XATTR_LIST); ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++ ++static int subdomain_inode_removexattr(struct dentry *dentry, char *name) ++{ ++ int error = 0; ++ ++ if (VALID_FSTYPE(dentry->d_inode)) { ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ error = sd_xattr(sd, dentry, name, SD_XATTR_REMOVE); ++ put_sdcopy(sd); ++ } ++ ++ return error; ++} ++ ++static int subdomain_file_permission(struct file *file, int mask) ++{ ++ struct subdomain sdcopy, *sd; ++ struct sdprofile *f_profile; ++ int error = 0; ++ ++ f_profile = SD_PROFILE(file->f_security); ++ /* bail out early if this isn't a mediated file */ ++ if (!(f_profile && VALID_FSTYPE(file->f_dentry->d_inode))) ++ goto out; ++ ++ sd = get_sdcopy(&sdcopy); ++ if (__sd_is_confined(sd) && f_profile != sd->active) ++ error = sd_perm(sd, file->f_dentry, file->f_vfsmnt, ++ mask & (MAY_EXEC | MAY_WRITE | MAY_READ)); ++ put_sdcopy(sd); ++ ++out: ++ return error; ++} ++ ++static int subdomain_file_alloc_security(struct file *file) ++{ ++ struct subdomain sdcopy, *sd; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ if (__sd_is_confined(sd)) ++ file->f_security = get_sdprofile(sd->active); ++ ++ put_sdcopy(sd); ++ ++ return 0; ++} ++ ++static void subdomain_file_free_security(struct file *file) ++{ ++ struct sdprofile *p = SD_PROFILE(file->f_security); ++ put_sdprofile(p); ++} ++ ++static int subdomain_file_mmap (struct file *file, unsigned long reqprot, ++ unsigned long prot, unsigned long flags) ++{ ++ int error = 0, mask = 0; ++ struct subdomain sdcopy, *sd; ++ struct sdprofile *f_profile; ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ f_profile = file ? SD_PROFILE(file->f_security) : NULL; ++ ++ if (prot & PROT_READ) ++ mask |= MAY_READ; ++ if (prot & PROT_WRITE) ++ mask |= MAY_WRITE; ++ if (prot & PROT_EXEC) ++ mask |= MAY_EXEC; ++ ++ SD_DEBUG("%s: 0x%x\n", __FUNCTION__, mask); ++ ++ /* Don't check if no subdomain's, profiles haven't changed, or ++ * mapping in the executable ++ */ ++ if (file && __sd_sub_defined(sd) && ++ f_profile != sd->active && ++ !(flags & MAP_EXECUTABLE)) ++ error = sd_perm(sd, file->f_dentry, file->f_vfsmnt, mask); ++ ++ put_sdcopy(sd); ++ ++ return error; ++} ++ ++static int subdomain_task_alloc_security(struct task_struct *p) ++{ ++ return sd_fork(p); ++} ++ ++static void subdomain_task_free_security(struct task_struct *p) ++{ ++ sd_release(p); ++} ++ ++static int subdomain_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, ++ int flags) ++{ ++ return cap_task_post_setuid(id0, id1, id2, flags); ++} ++ ++static void subdomain_task_reparent_to_init(struct task_struct *p) ++{ ++ cap_task_reparent_to_init(p); ++ return; ++} ++ ++static int subdomain_getprocattr(struct task_struct *p, char *name, void *value, ++ size_t size) ++{ ++ int error; ++ struct subdomain sdcopy, *sd; ++ char *str = value; ++ unsigned long flags; ++ ++ /* Subdomain only supports the "current" process attribute */ ++ if (strcmp(name, "current") != 0) { ++ error = -EINVAL; ++ goto out; ++ } ++ ++ if (!size) { ++ error = -ERANGE; ++ goto out; ++ } ++ ++ /* must be task querying itself or admin */ ++ if (current != p && !capable(CAP_SYS_ADMIN)) { ++ error = -EPERM; ++ goto out; ++ } ++ ++ read_lock_irqsave(&sd_lock, flags); ++ ++ sd = __get_sdcopy(&sdcopy, p); ++ ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ error = sd_getprocattr(sd, str, size); ++ put_sdcopy(sd); ++ ++out: ++ return error; ++} ++ ++static int subdomain_setprocattr(struct task_struct *p, char *name, void *value, ++ size_t size) ++{ ++ const char *cmd_changehat = "changehat ", ++ *cmd_setprofile = "setprofile "; ++ ++ int error = -EACCES; /* default to a perm denied */ ++ char *cmd = (char *)value; ++ ++ /* only support messages to current */ ++ if (strcmp(name, "current") != 0) { ++ error = -EINVAL; ++ goto out; ++ } ++ ++ if (!size) { ++ error = -ERANGE; ++ goto out; ++ } ++ ++ /* CHANGE HAT */ ++ if (size > strlen(cmd_changehat) && ++ strncmp(cmd, cmd_changehat, strlen(cmd_changehat)) == 0) { ++ char *hatinfo = cmd + strlen(cmd_changehat); ++ size_t infosize = size - strlen(cmd_changehat); ++ ++ /* Only the current process may change it's hat */ ++ if (current != p) { ++ SD_WARN("%s: Attempt by foreign task %s(%d) " ++ "[user %d] to changehat of task %s(%d)\n", ++ __FUNCTION__, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ ++ error = -EACCES; ++ goto out; ++ } ++ ++ error = sd_setprocattr_changehat(hatinfo, infosize); ++ if (error == 0) ++ /* success, set return to #bytes in orig request */ ++ error = size; ++ ++ /* SET NEW PROFILE */ ++ } else if (size > strlen(cmd_setprofile) && ++ strncmp(cmd, cmd_setprofile, strlen(cmd_setprofile)) == 0) { ++ int confined; ++ unsigned long flags; ++ ++ /* only an unconfined process with admin capabilities ++ * may change the profile of another task ++ */ ++ ++ if (!capable(CAP_SYS_ADMIN)) { ++ SD_WARN("%s: Unprivileged attempt by task %s(%d) " ++ "[user %d] to assign profile to task %s(%d)\n", ++ __FUNCTION__, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ error = -EACCES; ++ goto out; ++ } ++ ++ read_lock_irqsave(&sd_lock, flags); ++ confined = sd_is_confined(); ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ if (!confined) { ++ char *profile = cmd + strlen(cmd_setprofile); ++ size_t profilesize = size - strlen(cmd_setprofile); ++ ++ error = sd_setprocattr_setprofile(p, profile, profilesize); ++ if (error == 0) ++ /* success, ++ * set return to #bytes in orig request ++ */ ++ error = size; ++ } else { ++ SD_WARN("%s: Attempt by confined task %s(%d) " ++ "[user %d] to assign profile to task %s(%d)\n", ++ __FUNCTION__, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ ++ error = -EACCES; ++ } ++ } else { ++ /* unknown operation */ ++ SD_WARN("%s: Unknown setprocattr command '%.*s' by task %s(%d) " ++ "[user %d] for task %s(%d)\n", ++ __FUNCTION__, ++ size < 16 ? (int)size : 16, ++ cmd, ++ current->comm, ++ current->pid, ++ current->uid, ++ p->comm, ++ p->pid); ++ ++ error = -EINVAL; ++ } ++ ++out: ++ return error; ++} ++ ++struct security_operations subdomain_ops = { ++ .ptrace = subdomain_ptrace, ++ .capget = subdomain_capget, ++ .capset_check = subdomain_capset_check, ++ .capset_set = subdomain_capset_set, ++ .sysctl = subdomain_sysctl, ++ .capable = subdomain_capable, ++ .syslog = subdomain_syslog, ++ ++ .netlink_send = subdomain_netlink_send, ++ .netlink_recv = subdomain_netlink_recv, ++ ++ .bprm_apply_creds = subdomain_bprm_apply_creds, ++ .bprm_set_security = subdomain_bprm_set_security, ++ ++ .sb_mount = subdomain_sb_mount, ++ .sb_umount = subdomain_umount, ++ ++ .inode_mkdir = subdomain_inode_mkdir, ++ .inode_rmdir = subdomain_inode_rmdir, ++ .inode_create = subdomain_inode_create, ++ .inode_link = subdomain_inode_link, ++ .inode_unlink = subdomain_inode_unlink, ++ .inode_mknod = subdomain_inode_mknod, ++ .inode_rename = subdomain_inode_rename, ++ .inode_permission = subdomain_inode_permission, ++ .inode_setattr = subdomain_inode_setattr, ++ .inode_setxattr = subdomain_inode_setxattr, ++ .inode_getxattr = subdomain_inode_getxattr, ++ .inode_listxattr = subdomain_inode_listxattr, ++ .inode_removexattr = subdomain_inode_removexattr, ++ .file_permission = subdomain_file_permission, ++ .file_alloc_security = subdomain_file_alloc_security, ++ .file_free_security = subdomain_file_free_security, ++ .file_mmap = subdomain_file_mmap, ++ ++ .task_alloc_security = subdomain_task_alloc_security, ++ .task_free_security = subdomain_task_free_security, ++ .task_post_setuid = subdomain_task_post_setuid, ++ .task_reparent_to_init = subdomain_task_reparent_to_init, ++ ++ .getprocattr = subdomain_getprocattr, ++ .setprocattr = subdomain_setprocattr, ++}; ++ ++static int __init subdomain_init(void) ++{ ++ int error = 0; ++ const char *complainmsg = ": complainmode enabled"; ++ ++ if (!create_subdomainfs()) { ++ SD_ERROR("Unable to activate AppArmor filesystem\n"); ++ error = -ENOENT; ++ goto createfs_out; ++ } ++ ++ if (!alloc_nullprofiles()){ ++ SD_ERROR("Unable to allocate null profiles\n"); ++ error = -ENOMEM; ++ goto createfs_out; ++ } ++ ++ if ((error = register_security(&subdomain_ops))) { ++ SD_WARN("Unable to load AppArmor\n"); ++ goto dealloc_out; ++ } ++ ++ SD_INFO("AppArmor (version %s) initialized%s\n", ++ apparmor_version(), ++ subdomain_complain ? complainmsg : ""); ++ sd_audit_message(NULL, GFP_KERNEL, 0, ++ "AppArmor (version %s) initialized%s\n", ++ apparmor_version(), ++ subdomain_complain ? complainmsg : ""); ++ ++ return error; ++ ++dealloc_out: ++ free_nullprofiles(); ++ (void)destroy_subdomainfs(); ++ ++createfs_out: ++ return error; ++ ++} ++ ++static int subdomain_exit_removeall_iter(struct subdomain *sd, void *cookie) ++{ ++ /* write_lock(&sd_lock) held here */ ++ ++ if (__sd_is_confined(sd)) { ++ SD_DEBUG("%s: Dropping profiles %s(%d) " ++ "profile %s(%p) active %s(%p)\n", ++ __FUNCTION__, ++ sd->task->comm, sd->task->pid, ++ sd->profile->name, sd->profile, ++ sd->active->name, sd->active); ++ sd_switch_unconfined(sd); ++ } ++ ++ return 0; ++} ++ ++static void __exit subdomain_exit(void) ++{ ++ unsigned long flags; ++ ++ /* Remove profiles from the global profile list. ++ * This is just for tidyness as there is no way to reference this ++ * list once the AppArmor lsm hooks are detached (below) ++ */ ++ sd_profilelist_release(); ++ ++ /* Remove profiles from active tasks ++ * If this is not done, if module is reloaded after being removed, ++ * old profiles (still refcounted in memory) will become 'magically' ++ * reattached ++ */ ++ ++ write_lock_irqsave(&sd_lock, flags); ++ sd_subdomainlist_iterate(subdomain_exit_removeall_iter, NULL); ++ write_unlock_irqrestore(&sd_lock, flags); ++ ++ /* Free up list of active subdomain */ ++ sd_subdomainlist_release(); ++ ++ free_nullprofiles(); ++ ++ if (!destroy_subdomainfs()) ++ SD_WARN("Unable to properly deactivate AppArmor fs\n"); ++ ++ if (unregister_security(&subdomain_ops)) ++ SD_WARN("Unable to properly unregister AppArmor\n"); ++ ++ SD_INFO("AppArmor protection removed\n"); ++ sd_audit_message(NULL, GFP_KERNEL, 0, ++ "AppArmor protection removed\n"); ++} ++ ++security_initcall(subdomain_init); ++module_exit(subdomain_exit); ++ ++MODULE_DESCRIPTION("AppArmor process confinement"); ++MODULE_AUTHOR("Tony Jones "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/main.c +@@ -0,0 +1,1648 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor Core ++ */ ++ ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "aamatch/match.h" ++ ++#include "inline.h" ++ ++/* NULL profile ++ * ++ * Used when an attempt is made to changehat into a non-existant ++ * subhat. In the NULL profile, no file access is allowed ++ * (currently full network access is allowed). Using a NULL ++ * profile ensures that active is always non zero. ++ * ++ * Leaving the NULL profile is by either successfully changehatting ++ * into a sibling hat, or changehatting back to the parent (NULL hat). ++ */ ++struct sdprofile *null_profile; ++ ++/* NULL complain profile ++ * ++ * Used when in complain mode, to emit Permitting messages for non-existant ++ * profiles and hats. This is necessary because of selective mode, in which ++ * case we need a complain null_profile and enforce null_profile ++ * ++ * The null_complain_profile cannot be statically allocated, because it ++ * can be associated to files which keep their reference even if subdomain is ++ * unloaded ++ */ ++struct sdprofile *null_complain_profile; ++ ++/*************************** ++ * PRIVATE UTILITY FUNCTIONS ++ **************************/ ++ ++/** ++ * sd_taskattr_access: ++ * @name: name of file to check permission ++ * @mask: permission mask requested for file ++ * ++ * Determine if request is for write access to /proc/self/attr/current ++ */ ++static inline int sd_taskattr_access(const char *procrelname) ++{ ++/* ++ * assumes a 32bit pid, which requires max 10 decimal digits to represent ++ * sizeof includes trailing \0 ++ */ ++ char buf[sizeof("/attr/current") + 10]; ++ const int maxbuflen = sizeof(buf); ++ ++ snprintf(buf, maxbuflen, "%d/attr/current", current->pid); ++ buf[maxbuflen - 1] = 0; ++ ++ return strcmp(buf, procrelname) == 0; ++} ++ ++/** ++ * sd_file_mode - get full mode for file entry from profile ++ * @profile: profile ++ * @name: filename ++ */ ++static inline int sd_file_mode(struct sdprofile *profile, const char *name) ++{ ++ struct sd_entry *entry; ++ int mode = 0; ++ ++ SD_DEBUG("%s: %s\n", __FUNCTION__, name); ++ if (!name) { ++ SD_DEBUG("%s: no name\n", __FUNCTION__); ++ goto out; ++ } ++ ++ if (!profile) { ++ SD_DEBUG("%s: no profile\n", __FUNCTION__); ++ goto out; ++ } ++ list_for_each_entry(entry, &profile->file_entry, list) { ++ if (sdmatch_match(name, entry->filename, ++ entry->entry_type, entry->extradata)) ++ mode |= entry->mode; ++ } ++out: ++ return mode; ++} ++ ++/** ++ * sd_get_execmode - calculate what qualifier to apply to an exec ++ * @sd: subdomain to search ++ * @name: name of file to exec ++ * @xmod: pointer to a execution mode bit for the rule that was matched ++ * if the rule has no execuition qualifier {pui} then ++ * SD_MAY_EXEC is returned indicating a naked x ++ * if the has an exec qualifier then only the qualifier bit {pui} ++ * is returned (SD_MAY_EXEC) is not set. ++ * ++ * Returns 0 (false): ++ * if unable to find profile or there are conflicting pattern matches. ++ * *xmod - is not modified ++ * ++ * Returns 1 (true): ++ * if not confined ++ * *xmod = SD_MAY_EXEC ++ * if exec rule matched ++ * if the rule has an execution mode qualifier {pui} then ++ * *xmod = the execution qualifier of the rule {pui} ++ * else ++ * *xmod = SD_MAY_EXEC ++ */ ++static inline int sd_get_execmode(struct subdomain *sd, const char *name, ++ int *xmod) ++{ ++ struct sdprofile *profile; ++ struct sd_entry *entry; ++ struct sd_entry *match = NULL; ++ ++ int pattern_match_invalid = 0, rc = 0; ++ ++ /* not confined */ ++ if (!__sd_is_confined(sd)) { ++ SD_DEBUG("%s: not confined\n", __FUNCTION__); ++ goto not_confined; ++ } ++ ++ profile = sd->active; ++ ++ /* search list of profiles with 'x' permission ++ * this will also include entries with 'p', 'u' and 'i' ++ * qualifiers. ++ * ++ * If we find a pattern match we will keep looking for an exact match ++ * If we find conflicting pattern matches we will flag (while still ++ * looking for an exact match). If all we have is a conflict, FALSE ++ * is returned. ++ */ ++ ++ list_for_each_entry(entry, &profile->file_entryp[POS_SD_MAY_EXEC], ++ listp[POS_SD_MAY_EXEC]) { ++ if (!pattern_match_invalid && ++ entry->entry_type == sd_entry_pattern && ++ sdmatch_match(name, entry->filename, ++ entry->entry_type, entry->extradata)) { ++ if (match && ++ SD_EXEC_MASK(entry->mode) != ++ SD_EXEC_MASK(match->mode)) ++ pattern_match_invalid = 1; ++ else ++ /* keep searching for an exact match */ ++ match = entry; ++ } else if ((entry->entry_type == sd_entry_literal || ++ (!pattern_match_invalid && ++ entry->entry_type == sd_entry_tailglob)) && ++ sdmatch_match(name, entry->filename, ++ entry->entry_type, ++ entry->extradata)) { ++ if (entry->entry_type == sd_entry_literal) { ++ /* got an exact match -- there can be only ++ * one, asserted at profile load time ++ */ ++ match = entry; ++ pattern_match_invalid = 0; ++ break; ++ } else { ++ if (match && ++ SD_EXEC_MASK(entry->mode) != ++ SD_EXEC_MASK(match->mode)) ++ pattern_match_invalid = 1; ++ else ++ /* got a tailglob match, keep searching ++ * for an exact match ++ */ ++ match = entry; ++ } ++ } ++ ++ } ++ ++ rc = match && !pattern_match_invalid; ++ ++ if (rc) { ++ int mode = SD_EXEC_MASK(match->mode); ++ ++ /* check for qualifiers, if present ++ * we just return the qualifier ++ */ ++ if (mode & ~SD_MAY_EXEC) ++ mode = mode & ~SD_MAY_EXEC; ++ ++ *xmod = mode; ++ } else if (!match) { ++ SD_DEBUG("%s: Unable to find execute entry in profile " ++ "for image '%s'\n", ++ __FUNCTION__, ++ name); ++ } else if (pattern_match_invalid) { ++ SD_WARN("%s: Inconsistency in profile %s. " ++ "Two (or more) patterns specify conflicting exec " ++ "qualifiers ('u', 'i' or 'p') for image %s\n", ++ __FUNCTION__, ++ sd->active->name, ++ name); ++ } ++ ++ return rc; ++ ++not_confined: ++ *xmod = SD_MAY_EXEC; ++ return 1; ++} ++ ++/** ++ * sd_filter_mask ++ * @mask: requested mask ++ * @inode: potential directory inode ++ * ++ * This fn performs pre-verification of the requested mask ++ * We ignore append. Previously we required 'w' on a dir to add a file. ++ * No longer. Now we require 'w' on just the file itself. Traversal 'x' is ++ * also ignored for directories. ++ * ++ * Returned value of 0 indicates no need to perform a perm check. ++ */ ++static inline int sd_filter_mask(int mask, struct inode *inode) ++{ ++ if (mask) { ++ int elim = MAY_APPEND; ++ ++ if (inode && S_ISDIR(inode->i_mode)) ++ elim |= (MAY_EXEC | MAY_WRITE); ++ ++ mask &= ~elim; ++ } ++ ++ return mask; ++} ++ ++static inline void sd_permerror2result(int perm_result, struct sd_audit *sa) ++{ ++ if (perm_result == 0) { /* success */ ++ sa->result = 1; ++ sa->errorcode = 0; ++ } else { /* -ve internal error code or +ve mask of denied perms */ ++ sa->result = 0; ++ sa->errorcode = perm_result; ++ } ++} ++ ++/************************* ++ * MAIN INTERNAL FUNCTIONS ++ ************************/ ++ ++/** ++ * sd_file_perm - calculate access mode for file ++ * @subdomain: current subdomain ++ * @name: name of file to calculate mode for ++ * @mask: permission mask requested for file ++ * ++ * Search the sd_entry list in @profile. ++ * Search looking to verify all permissions passed in mask. ++ * Perform the search by looking at the partitioned list of entries, one ++ * partition per permission bit. ++ * ++ * Return 0 on success, else mask of non-allowed permissions ++ */ ++static unsigned int sd_file_perm(struct subdomain *sd, const char *name, ++ int mask) ++{ ++ struct sdprofile *profile; ++ int i, error = 0, mode; ++ ++#define PROCPFX "/proc/" ++#define PROCLEN sizeof(PROCPFX) - 1 ++ ++ SD_DEBUG("%s: %s 0x%x\n", __FUNCTION__, name, mask); ++ ++ /* should not enter with other than R/W/X/L */ ++ BUG_ON(mask & ++ ~(SD_MAY_READ | SD_MAY_WRITE | SD_MAY_EXEC | SD_MAY_LINK)); ++ ++ /* not confined */ ++ if (!__sd_is_confined(sd)) { ++ /* exit with access allowed */ ++ SD_DEBUG("%s: not confined\n", __FUNCTION__); ++ goto done; ++ } ++ ++ /* Special case access to /proc/self/attr/current ++ * Currently we only allow access if opened O_WRONLY ++ */ ++ if (mask == MAY_WRITE && strncmp(PROCPFX, name, PROCLEN) == 0 && ++ (!list_empty(&sd->profile->sub) || SUBDOMAIN_COMPLAIN(sd)) && ++ sd_taskattr_access(name + PROCLEN)) ++ goto done; ++ ++ profile = sd->active; ++ ++ mode = 0; ++ ++ /* iterate over partition, one permission bit at a time */ ++ for (i = 0; i <= POS_SD_FILE_MAX; i++) { ++ struct sd_entry *entry; ++ ++ /* do we have to accumulate this bit? ++ * or have we already accumulated it (shortcut below)? */ ++ if (!(mask & (1 << i)) || mode & (1 << i)) ++ continue; ++ ++ list_for_each_entry(entry, &profile->file_entryp[i], ++ listp[i]) { ++ if (sdmatch_match(name, entry->filename, ++ entry->entry_type, entry->extradata)) { ++ /* Shortcut, accumulate all bits present */ ++ mode |= entry->mode; ++ ++ /* ++ * Mask bits are overloaded ++ * MAY_{EXEC,WRITE,READ,APPEND} are used by ++ * kernel, other values are used locally only. ++ */ ++ if ((mode & mask) == mask) { ++ SD_DEBUG("MATCH! %s=0x%x [total mode=0x%x]\n", ++ name, mask, mode); ++ ++ goto done; ++ } ++ } ++ } ++ } ++ ++ /* return permissions not satisfied */ ++ error = mask & ~mode; ++ ++done: ++ return error; ++} ++ ++/** ++ * sd_link_perm - test permission to link to a file ++ * @sd: current subdomain ++ * @link: name of link being created ++ * @target: name of target to be linked to ++ * ++ * Look up permission mode on both @link and @target. @link must have same ++ * permission mode as @target. At least @link must have the link bit enabled. ++ * Return 0 on success, error otherwise. ++ */ ++static int sd_link_perm(struct subdomain *sd, ++ const char *link, const char *target) ++{ ++ int l_mode, t_mode, ret; ++ struct sdprofile *profile = sd->active; ++ ++ l_mode = sd_file_mode(profile, link); ++ if (l_mode & SD_MAY_LINK) { ++ /* mask off link bit */ ++ l_mode &= ~SD_MAY_LINK; ++ ++ t_mode = sd_file_mode(profile, target); ++ t_mode &= ~SD_MAY_LINK; ++ ++ ret = (l_mode == t_mode); ++ } else { ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/** ++ * _sd_perm_dentry ++ * @sd: current subdomain ++ * @dentry: requested dentry ++ * @mask: mask of requested operations ++ * @pname: pointer to hold matched pathname (if any) ++ * ++ * Helper function. Obtain pathname for specified dentry. Verify if profile ++ * authorizes mask operations on pathname (due to lack of vfsmnt it is sadly ++ * necessary to search mountpoints in namespace -- when nameidata is passed ++ * more fully, this code can go away). If more than one mountpoint matches ++ * but none satisfy the profile, only the first pathname (mountpoint) is ++ * returned for subsequent logging. ++ * ++ * Return 0 (success), +ve (mask of permissions not satisfied) or -ve (system ++ * error, most likely -ENOMEM). ++ */ ++static int _sd_perm_dentry(struct subdomain *sd, struct dentry *dentry, ++ int mask, const char **pname) ++{ ++ char *name = NULL, *failed_name = NULL; ++ struct sd_path_data data; ++ int error = 0, failed_error = 0, sdpath_error, ++ sdcomplain = SUBDOMAIN_COMPLAIN(sd); ++ ++ /* search all paths to dentry */ ++ ++ sd_path_begin(dentry, &data); ++ do { ++ name = sd_path_getname(&data); ++ if (name) { ++ /* error here is 0 (success) or +ve (mask of perms) */ ++ error = sd_file_perm(sd, name, mask); ++ ++ /* access via any path is enough */ ++ if (sdcomplain || error == 0) ++ break; /* Caller must free name */ ++ ++ /* Already have an path that failed? */ ++ if (failed_name) { ++ sd_put_name(name); ++ } else { ++ failed_name = name; ++ failed_error = error; ++ } ++ } ++ } while (name); ++ ++ if ((sdpath_error = sd_path_end(&data)) != 0) { ++ SD_ERROR("%s: An error occured while translating dentry %p " ++ "inode# %lu to a pathname. Error %d\n", ++ __FUNCTION__, ++ dentry, ++ dentry->d_inode->i_ino, ++ sdpath_error); ++ ++ WARN_ON(name); /* name should not be set if error */ ++ error = sdpath_error; ++ name = NULL; ++ } else if (name) { ++ if (failed_name) ++ sd_put_name(failed_name); ++ } else { ++ name = failed_name; ++ error = failed_error; ++ } ++ ++ *pname = name; ++ ++ return error; ++} ++ ++/************************** ++ * GLOBAL UTILITY FUNCTIONS ++ *************************/ ++ ++/** ++ * alloc_nullprofiles - Allocate null profiles ++ */ ++int alloc_nullprofiles(void) ++{ ++ null_profile = alloc_sdprofile(); ++ null_complain_profile = alloc_sdprofile(); ++ ++ if (!null_profile || !null_complain_profile) ++ goto fail; ++ ++ null_profile->name = kstrdup("null-profile", GFP_KERNEL); ++ null_complain_profile->name = ++ kstrdup("null-complain-profile", GFP_KERNEL); ++ ++ if (!null_profile->name || ++ !null_complain_profile->name) ++ goto fail; ++ ++ get_sdprofile(null_profile); ++ get_sdprofile(null_complain_profile); ++ null_complain_profile->flags.complain = 1; ++ ++ return 1; ++ ++fail: ++ /* free_sdprofile is safe for freeing partially constructed objects */ ++ free_sdprofile(null_profile); ++ free_sdprofile(null_complain_profile); ++ null_profile = null_complain_profile = NULL; ++ return 0; ++} ++ ++/** ++ * free_nullprofiles - Free null profiles ++ */ ++void free_nullprofiles(void) ++{ ++ put_sdprofile(null_complain_profile); ++ put_sdprofile(null_profile); ++ null_profile = null_complain_profile = NULL; ++} ++ ++/** ++ * sd_audit_message - Log a message to the audit subsystem ++ * @sd: current subdomain ++ * @gfp: allocation flags ++ * @flags: audit flags ++ * @fmt: varargs fmt ++ */ ++int sd_audit_message(struct subdomain *sd, unsigned int gfp, int flags, ++ const char *fmt, ...) ++{ ++ int ret; ++ struct sd_audit sa; ++ ++ sa.type = SD_AUDITTYPE_MSG; ++ sa.name = fmt; ++ va_start(sa.vaval, fmt); ++ sa.flags = flags; ++ sa.gfp_mask = gfp; ++ sa.errorcode = 0; ++ sa.result = 0; /* fake failure: force message to be logged */ ++ ++ ret = sd_audit(sd, &sa); ++ ++ va_end(sa.vaval); ++ ++ return ret; ++} ++ ++/** ++ * sd_audit_syscallreject - Log a syscall rejection to the audit subsystem ++ * @sd: current subdomain ++ * @msg: string describing syscall being rejected ++ * @gfp: memory allocation flags ++ */ ++int sd_audit_syscallreject(struct subdomain *sd, unsigned int gfp, ++ const char *msg) ++{ ++ struct sd_audit sa; ++ ++ sa.type = SD_AUDITTYPE_SYSCALL; ++ sa.name = msg; ++ sa.flags = 0; ++ sa.gfp_mask = gfp; ++ sa.errorcode = 0; ++ sa.result = 0; /* failure */ ++ ++ return sd_audit(sd, &sa); ++} ++ ++/** ++ * sd_audit - Log an audit event to the audit subsystem ++ * @sd: current subdomain ++ * @sa: audit event ++ */ ++int sd_audit(struct subdomain *sd, const struct sd_audit *sa) ++{ ++ struct audit_buffer *ab = NULL; ++ struct audit_context *ctx; ++ ++ const char *logcls; ++ unsigned int flags; ++ int sdaudit = 0, ++ sdcomplain = 0, ++ error = -EINVAL, ++ opspec_error = -EACCES; ++ ++ const unsigned int gfp_mask = sa->gfp_mask; ++ ++ WARN_ON(sa->type >= SD_AUDITTYPE__END); ++ ++ /* ++ * sa->result: 1 success, 0 failure ++ * sa->errorcode: success: 0 ++ * failure: +ve mask of failed permissions or -ve ++ * system error ++ */ ++ ++ if (likely(sa->result)) { ++ if (likely(!SUBDOMAIN_AUDIT(sd))) { ++ /* nothing to log */ ++ error = 0; ++ goto out; ++ } else { ++ sdaudit = 1; ++ logcls = "AUDITING"; ++ } ++ } else if (sa->errorcode < 0) { ++ audit_log(current->audit_context, gfp_mask, AUDIT_SD, ++ "Internal error auditing event type %d (error %d)\n", ++ sa->type, sa->errorcode); ++ SD_ERROR("Internal error auditing event type %d (error %d)\n", ++ sa->type, sa->errorcode); ++ error = sa->errorcode; ++ goto out; ++ } else if (sa->type == SD_AUDITTYPE_SYSCALL) { ++ /* Currently SD_AUDITTYPE_SYSCALL is for rejects only. ++ * Values set by sd_audit_syscallreject will get us here. ++ */ ++ logcls = "REJECTING"; ++ } else { ++ sdcomplain = SUBDOMAIN_COMPLAIN(sd); ++ logcls = sdcomplain ? "PERMITTING" : "REJECTING"; ++ } ++ ++ /* In future extend w/ per-profile flags ++ * (flags |= sa->active->flags) ++ */ ++ flags = sa->flags; ++ if (subdomain_logsyscall) ++ flags |= SD_AUDITFLAG_AUDITSS_SYSCALL; ++ ++ ++ /* Force full audit syscall logging regardless of global setting if ++ * we are rejecting a syscall ++ */ ++ if (sa->type == SD_AUDITTYPE_SYSCALL) { ++ ctx = current->audit_context; ++ } else { ++ ctx = (flags & SD_AUDITFLAG_AUDITSS_SYSCALL) ? ++ current->audit_context : NULL; ++ } ++ ++ ab = audit_log_start(ctx, gfp_mask, AUDIT_SD); ++ ++ if (!ab) { ++ SD_ERROR("Unable to log event (%d) to audit subsys\n", ++ sa->type); ++ if (sdcomplain) ++ error = 0; ++ goto out; ++ } ++ ++ /* messages get special handling */ ++ if (sa->type == SD_AUDITTYPE_MSG) { ++ audit_log_vformat(ab, sa->name, sa->vaval); ++ audit_log_end(ab); ++ error = 0; ++ goto out; ++ } ++ ++ /* log operation */ ++ ++ audit_log_format(ab, "%s ", logcls); /* REJECTING/ALLOWING/etc */ ++ ++ if (sa->type == SD_AUDITTYPE_FILE) { ++ int perm = sdaudit ? sa->ival : sa->errorcode; ++ ++ audit_log_format(ab, "%s%s%s%s access to %s ", ++ perm & SD_MAY_READ ? "r" : "", ++ perm & SD_MAY_WRITE ? "w" : "", ++ perm & SD_MAY_EXEC ? "x" : "", ++ perm & SD_MAY_LINK ? "l" : "", ++ sa->name); ++ ++ opspec_error = -EPERM; ++ ++ } else if (sa->type == SD_AUDITTYPE_DIR) { ++ audit_log_format(ab, "%s on %s ", ++ sa->ival == SD_DIR_MKDIR ? "mkdir" : "rmdir", ++ sa->name); ++ ++ } else if (sa->type == SD_AUDITTYPE_ATTR) { ++ struct iattr *iattr = (struct iattr*)sa->pval; ++ ++ audit_log_format(ab, ++ "attribute (%s%s%s%s%s%s%s) change to %s ", ++ iattr->ia_valid & ATTR_MODE ? "mode," : "", ++ iattr->ia_valid & ATTR_UID ? "uid," : "", ++ iattr->ia_valid & ATTR_GID ? "gid," : "", ++ iattr->ia_valid & ATTR_SIZE ? "size," : "", ++ ((iattr->ia_valid & ATTR_ATIME_SET) || ++ (iattr->ia_valid & ATTR_ATIME)) ? "atime," : "", ++ ((iattr->ia_valid & ATTR_MTIME_SET) || ++ (iattr->ia_valid & ATTR_MTIME)) ? "mtime," : "", ++ iattr->ia_valid & ATTR_CTIME ? "ctime," : "", ++ sa->name); ++ ++ } else if (sa->type == SD_AUDITTYPE_XATTR) { ++ const char *fmt; ++ switch (sa->ival) { ++ case SD_XATTR_GET: ++ fmt = "xattr get"; ++ break; ++ case SD_XATTR_SET: ++ fmt = "xattr set"; ++ break; ++ case SD_XATTR_LIST: ++ fmt = "xattr list"; ++ break; ++ case SD_XATTR_REMOVE: ++ fmt = "xattr remove"; ++ break; ++ default: ++ fmt = "xattr "; ++ break; ++ } ++ ++ audit_log_format(ab, "%s on %s ", fmt, sa->name); ++ ++ } else if (sa->type == SD_AUDITTYPE_LINK) { ++ audit_log_format(ab, ++ "link access from %s to %s ", ++ sa->name, ++ (char*)sa->pval); ++ ++ } else if (sa->type == SD_AUDITTYPE_CAP) { ++ audit_log_format(ab, ++ "access to capability '%s' ", ++ capability_to_name(sa->ival)); ++ ++ opspec_error = -EPERM; ++ } else if (sa->type == SD_AUDITTYPE_SYSCALL) { ++ audit_log_format(ab, "access to syscall '%s' ", sa->name); ++ ++ opspec_error = -EPERM; ++ } else { ++ /* -EINVAL -- will WARN_ON above */ ++ goto out; ++ } ++ ++ audit_log_format(ab, "(%s(%d) ", current->comm, current->pid); ++ ++ if (0) ++ audit_log_format(ab, "[global deny])\n"); ++ else ++ audit_log_format(ab, "profile %s active %s)\n", ++ sd->profile->name, sd->active->name); ++ ++ audit_log_end(ab); ++ ++ if (sdcomplain) ++ error = 0; ++ else ++ error = sa->result ? 0 : opspec_error; ++ ++out: ++ return error; ++} ++ ++/** ++ * sd_get_name - retrieve fully qualified path name ++ * @dentry: relative path element ++ * @mnt: where in tree ++ * ++ * Returns fully qualified path name on sucess, NULL on failure. ++ * sd_put_name must be used to free allocated buffer. ++ */ ++char *sd_get_name(struct dentry *dentry, struct vfsmount *mnt) ++{ ++ char *page, *name = NULL; ++ ++ page = (char *)__get_free_page(GFP_KERNEL); ++ if (!page) ++ goto out; ++ ++ name = d_path(dentry, mnt, page, PAGE_SIZE); ++ ++ /* check for (deleted) that d_path appends to pathnames if the dentry ++ * has been removed from the cache. ++ * The size > deleted_size and strcmp checks are redundant safe guards. ++ */ ++ if (name) { ++ const char deleted_str[] = " (deleted)"; ++ const size_t deleted_size = sizeof(deleted_str) - 1; ++ size_t size; ++ size = strlen(name); ++ if (!IS_ROOT(dentry) && d_unhashed(dentry) && ++ size > deleted_size && ++ strcmp(name + size - deleted_size, deleted_str) == 0) ++ name[size - deleted_size] = '\0'; ++ } ++ SD_DEBUG("%s: full_path=%s\n", __FUNCTION__, name); ++out: ++ return name; ++} ++ ++/*********************************** ++ * GLOBAL PERMISSION CHECK FUNCTIONS ++ ***********************************/ ++ ++/** ++ * sd_attr - check whether attribute change allowed ++ * @sd: subdomain to check against to check against ++ * @dentry: file to check ++ * @iattr: attribute changes requested ++ */ ++int sd_attr(struct subdomain *sd, struct dentry *dentry, struct iattr *iattr) ++{ ++ int error = 0, permerror; ++ struct sd_audit sa; ++ ++ if (!__sd_is_confined(sd)) ++ goto out; ++ ++ sa.type = SD_AUDITTYPE_ATTR; ++ sa.pval = iattr; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _sd_perm_dentry(sd, dentry, MAY_WRITE, &sa.name); ++ sd_permerror2result(permerror, &sa); ++ ++ error = sd_audit(sd, &sa); ++ ++ sd_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++int sd_xattr(struct subdomain *sd, struct dentry *dentry, const char *xattr, ++ int xattroptype) ++{ ++ int error = 0, permerror, mask = 0; ++ struct sd_audit sa; ++ ++ /* if not confined or empty mask permission granted */ ++ if (!__sd_is_confined(sd)) ++ goto out; ++ ++ if (xattroptype == SD_XATTR_GET || xattroptype == SD_XATTR_LIST) ++ mask = MAY_READ; ++ else if (xattroptype == SD_XATTR_SET || xattroptype == SD_XATTR_REMOVE) ++ mask = MAY_WRITE; ++ ++ sa.type = SD_AUDITTYPE_XATTR; ++ sa.ival = xattroptype; ++ sa.pval = xattr; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _sd_perm_dentry(sd, dentry, mask, &sa.name); ++ sd_permerror2result(permerror, &sa); ++ ++ error = sd_audit(sd, &sa); ++ ++ sd_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * sd_perm - basic subdomain permissions check ++ * @sd: subdomain to check against ++ * @dentry: dentry ++ * @mnt: mountpoint ++ * @mask: access mode requested ++ * ++ * Determine if access (mask) for dentry is authorized by subdomain sd. ++ * Result, 0 (success), -ve (error) ++ */ ++int sd_perm(struct subdomain *sd, struct dentry *dentry, struct vfsmount *mnt, ++ int mask) ++{ ++ int error = 0, permerror; ++ struct sd_audit sa; ++ ++ if (!__sd_is_confined(sd)) ++ goto out; ++ ++ if ((mask = sd_filter_mask(mask, dentry->d_inode)) == 0) ++ goto out; ++ ++ sa.type = SD_AUDITTYPE_FILE; ++ sa.name = sd_get_name(dentry, mnt); ++ sa.ival = mask; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = (sa.name ? sd_file_perm(sd, sa.name, mask) : -ENOMEM); ++ ++ sd_permerror2result(permerror, &sa); ++ ++ error = sd_audit(sd, &sa); ++ ++ sd_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * sd_perm_nameidata: interface to sd_perm accepting nameidata ++ * @sd: subdomain to check against ++ * @nd: namespace data (for vfsmnt and dentry) ++ * @mask: access mode requested ++ */ ++int sd_perm_nameidata(struct subdomain *sd, struct nameidata *nd, int mask) ++{ ++ int error = 0; ++ ++ if (nd) ++ error = sd_perm(sd, nd->dentry, nd->mnt, mask); ++ ++ return error; ++} ++ ++/** ++ * sd_perm_dentry - file permissions interface when no vfsmnt available ++ * @sd: current subdomain ++ * @dentry: requested dentry ++ * @mask: access mode requested ++ * ++ * Determine if access (mask) for dentry is authorized by subdomain sd. ++ * Result, 0 (success), -ve (error) ++ */ ++int sd_perm_dentry(struct subdomain *sd, struct dentry *dentry, int mask) ++{ ++ int error = 0, permerror; ++ struct sd_audit sa; ++ ++ if (!__sd_is_confined(sd)) ++ goto out; ++ ++ if ((mask = sd_filter_mask(mask, dentry->d_inode)) == 0) ++ goto out; ++ ++ sa.type = SD_AUDITTYPE_FILE; ++ sa.ival = mask; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _sd_perm_dentry(sd, dentry, mask, &sa.name); ++ sd_permerror2result(permerror, &sa); ++ ++ error = sd_audit(sd, &sa); ++ ++ sd_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * sd_perm_dir ++ * @sd: current subdomain ++ * @dentry: requested dentry ++ * @mode: SD_DIR_MKDIR or SD_DIR_RMDIR ++ * ++ * Determine if directory operation (make/remove) for dentry is authorized ++ * by subdomain sd. ++ * Result, 0 (success), -ve (error) ++ */ ++int sd_perm_dir(struct subdomain *sd, struct dentry *dentry, int diroptype) ++{ ++ int error = 0, permerror, mask; ++ struct sd_audit sa; ++ ++ BUG_ON(diroptype != SD_DIR_MKDIR && diroptype != SD_DIR_RMDIR); ++ ++ if (!__sd_is_confined(sd)) ++ goto out; ++ ++ mask = MAY_WRITE; ++ ++ sa.type = SD_AUDITTYPE_DIR; ++ sa.ival = diroptype; ++ sa.flags = 0; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ permerror = _sd_perm_dentry(sd, dentry, mask, &sa.name); ++ sd_permerror2result(permerror, &sa); ++ ++ error = sd_audit(sd, &sa); ++ ++ sd_put_name(sa.name); ++ ++out: ++ return error; ++} ++ ++/** ++ * sd_capability - test permission to use capability ++ * @sd: subdomain to check against ++ * @cap: capability to be tested ++ * ++ * Look up capability in active profile capability set. ++ * Return 0 (success), -EPERM (error) ++ */ ++int sd_capability(struct subdomain *sd, int cap) ++{ ++ int error = 0; ++ ++ if (__sd_is_confined(sd)) { ++ struct sd_audit sa; ++ ++ sa.type = SD_AUDITTYPE_CAP; ++ sa.name = NULL; ++ sa.ival = cap; ++ sa.flags = 0; ++ sa.errorcode = 0; ++ sa.result = cap_raised(sd->active->capabilities, cap); ++ sa.gfp_mask = GFP_ATOMIC; ++ ++ error = sd_audit(sd, &sa); ++ } ++ ++ return error; ++} ++ ++/** ++ * sd_link - hard link check ++ * @link: dentry for link being created ++ * @target: dentry for link target ++ * @sd: subdomain to check against ++ * ++ * Checks link permissions for all possible name combinations. This is ++ * particularly ugly. Returns 0 on sucess, error otherwise. ++ */ ++int sd_link(struct subdomain *sd, struct dentry *link, struct dentry *target) ++{ ++ char *iname = NULL, *oname = NULL, ++ *failed_iname = NULL, *failed_oname = NULL; ++ unsigned int result = 0; ++ int error, sdpath_error, errorcode = 0, match = 0, ++ sdcomplain = SUBDOMAIN_COMPLAIN(sd); ++ struct sd_path_data idata, odata; ++ struct sd_audit sa; ++ ++ if (!__sd_is_confined(sd)) ++ return 0; ++ ++ /* Perform nested lookup for names. ++ * This is necessary in the case where /dev/block is mounted ++ * multiple times, i.e /dev/block->/a and /dev/block->/b ++ * This allows us to detect links where src/dest are on different ++ * mounts. N.B no support yet for links across bind mounts of ++ * the form mount -bind /mnt/subpath /mnt2 ++ * ++ * Getting direct access to vfsmounts (via nameidata) for link and ++ * target would allow all this uglyness to go away. ++ * ++ * If more than one mountpoint matches but none satisfy the profile, ++ * only the first pathname (mountpoint) is logged. ++ */ ++ ++ sd_path_begin2(target, link, &odata); ++ do { ++ oname = sd_path_getname(&odata); ++ if (oname) { ++ sd_path_begin(target, &idata); ++ do { ++ iname = sd_path_getname(&idata); ++ if (iname) { ++ result = sd_link_perm(sd, oname, iname); ++ ++ /* access via any path is enough */ ++ if (result || sdcomplain) { ++ match = 1; ++ break; ++ } ++ ++ /* Already have an path that failed? */ ++ if (failed_iname) { ++ sd_put_name(iname); ++ } else { ++ failed_iname = iname; ++ failed_oname = oname; ++ } ++ } ++ } while (iname && !match); ++ ++ /* should not be possible if we matched */ ++ if ((sdpath_error = sd_path_end(&idata)) != 0) { ++ SD_ERROR("%s: An error occured while " ++ "translating inner dentry %p " ++ "inode %lu to a pathname. Error %d\n", ++ __FUNCTION__, ++ target, ++ target->d_inode->i_ino, ++ sdpath_error); ++ ++ /* name should not be set if error */ ++ WARN_ON(iname); ++ ++ errorcode = sdpath_error; ++ } ++ ++ /* don't release if we're saving it */ ++ if (!match && failed_oname != oname) ++ sd_put_name(oname); ++ } ++ } while (oname && !match); ++ ++ if (errorcode != 0) { ++ /* inner error */ ++ (void)sd_path_end(&odata); ++ } else if ((sdpath_error = sd_path_end(&odata)) != 0) { ++ SD_ERROR("%s: An error occured while translating outer " ++ "dentry %p inode %lu to a pathname. Error %d\n", ++ __FUNCTION__, ++ link, ++ link->d_inode->i_ino, ++ sdpath_error); ++ ++ errorcode = sdpath_error; ++ } ++ ++ if (errorcode != 0) { ++ /* inner or outer error */ ++ result = 0; ++ } else if (match) { ++ result = 1; ++ } else { ++ /* failed to match */ ++ WARN_ON(iname); ++ WARN_ON(oname); ++ ++ result = 0; ++ iname = failed_iname; ++ oname = failed_oname; ++ } ++ ++ sa.type = SD_AUDITTYPE_LINK; ++ sa.name = oname; /* link */ ++ sa.pval = iname; /* target */ ++ sa.flags = 0; ++ sa.errorcode = errorcode; ++ sa.result = result; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ error = sd_audit(sd, &sa); ++ ++ if (failed_oname != oname) ++ sd_put_name(failed_oname); ++ if (failed_iname != iname) ++ sd_put_name(failed_iname); ++ ++ sd_put_name(oname); ++ sd_put_name(iname); ++ ++ return error; ++} ++ ++/********************************** ++ * GLOBAL PROCESS RELATED FUNCTIONS ++ *********************************/ ++ ++/** ++ * sd_fork - create a new subdomain ++ * @p: new process ++ * ++ * Create a new subdomain struct for the newly created process @p. ++ * Copy parent info to child. If parent has no subdomain, child ++ * will get one with NULL values. Return 0 on sucess. ++ */ ++ ++int sd_fork(struct task_struct *p) ++{ ++ struct subdomain *sd = SD_SUBDOMAIN(current->security); ++ struct subdomain *newsd = alloc_subdomain(p); ++ ++ SD_DEBUG("%s\n", __FUNCTION__); ++ ++ if (!newsd) ++ return -ENOMEM; ++ ++ if (sd) { ++ unsigned long flags; ++ ++ /* Can get away with a read rather than write lock here ++ * as we just allocated newsd above, so we can guarantee ++ * that it's active/profile are null and therefore a replace ++ * cannot happen. ++ */ ++ read_lock_irqsave(&sd_lock, flags); ++ sd_switch(newsd, sd->profile, sd->active); ++ newsd->sd_hat_magic = sd->sd_hat_magic; ++ read_unlock_irqrestore(&sd_lock, flags); ++ ++ if (SUBDOMAIN_COMPLAIN(sd) && ++ sd->active == null_complain_profile) ++ LOG_HINT(sd, GFP_KERNEL, HINT_FORK, ++ "pid=%d child=%d\n", ++ current->pid, p->pid); ++ } ++ p->security = newsd; ++ return 0; ++} ++ ++/** ++ * sd_register - register a new program ++ * @filp: file of program being registered ++ * ++ * Try to register a new program during execve(). This should give the ++ * new program a valid subdomain. ++ * ++ * This _used_ to be a really simple piece of code :-( ++ * ++ */ ++int sd_register(struct file *filp) ++{ ++ char *filename; ++ struct subdomain *sd, sdcopy; ++ struct sdprofile *newprofile = NULL, unconstrained_flag; ++ int error = -ENOMEM, ++ exec_mode = 0, ++ findprofile = 0, ++ findprofile_mandatory = 0, ++ issdcopy = 1, ++ complain = 0; ++ ++ SD_DEBUG("%s\n", __FUNCTION__); ++ ++ sd = get_sdcopy(&sdcopy); ++ ++ if (sd) { ++ complain = SUBDOMAIN_COMPLAIN(sd); ++ } else { ++ /* task has no subdomain. This can happen when a task is ++ * created when subdomain is not loaded. Allocate and ++ * attach a subdomain to the task ++ */ ++ issdcopy = 0; ++ ++ sd = alloc_subdomain(current); ++ if (!sd) { ++ SD_WARN("%s: Failed to allocate subdomain\n", ++ __FUNCTION__); ++ goto out; ++ } ++ ++ current->security = sd; ++ } ++ ++ filename = sd_get_name(filp->f_dentry, filp->f_vfsmnt); ++ if (!filename) { ++ SD_WARN("%s: Failed to get filename\n", __FUNCTION__); ++ goto out; ++ } ++ ++ error = 0; ++ ++ if (!__sd_is_confined(sd)) { ++ /* Unconfined task, load profile if it exists */ ++ findprofile = 1; ++ goto find_profile; ++ } ++ ++ /* Confined task, determine what mode inherit, unconstrained or ++ * mandatory to load new profile ++ */ ++ if (sd_get_execmode(sd, filename, &exec_mode)) { ++ switch (exec_mode) { ++ case SD_EXEC_INHERIT: ++ /* do nothing - setting of profile ++ * already handed in sd_fork ++ */ ++ SD_DEBUG("%s: INHERIT %s\n", ++ __FUNCTION__, ++ filename); ++ break; ++ ++ case SD_EXEC_UNCONSTRAINED: ++ SD_DEBUG("%s: UNCONSTRAINED %s\n", ++ __FUNCTION__, ++ filename); ++ ++ /* unload profile */ ++ newprofile = &unconstrained_flag; ++ break; ++ ++ case SD_EXEC_PROFILE: ++ SD_DEBUG("%s: PROFILE %s\n", ++ __FUNCTION__, ++ filename); ++ ++ findprofile = 1; ++ findprofile_mandatory = 1; ++ break; ++ ++ case SD_MAY_EXEC: ++ /* this should not happen, entries ++ * with just EXEC only should be ++ * rejected at profile load time ++ */ ++ SD_ERROR("%s: Rejecting exec(2) of image '%s'. " ++ "SD_MAY_EXEC without exec qualifier invalid " ++ "(%s(%d) profile %s active %s\n", ++ __FUNCTION__, ++ filename, ++ current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ error = -EPERM; ++ break; ++ ++ default: ++ SD_ERROR("%s: Rejecting exec(2) of image '%s'. " ++ "Unknown exec qualifier %x " ++ "(%s (pid %d) profile %s active %s)\n", ++ __FUNCTION__, ++ filename, ++ exec_mode, ++ current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ error = -EPERM; ++ break; ++ } ++ ++ } else if (complain) { ++ /* There was no entry in calling profile ++ * describing mode to execute image in. ++ * Drop into null-profile ++ */ ++ newprofile = get_sdprofile(null_complain_profile); ++ } else { ++ SD_WARN("%s: Rejecting exec(2) of image '%s'. " ++ "Unable to determine exec qualifier " ++ "(%s (pid %d) profile %s active %s)\n", ++ __FUNCTION__, ++ filename, ++ current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ error = -EPERM; ++ } ++ ++ ++find_profile: ++ if (!findprofile) ++ goto apply_profile; ++ ++ /* Locate new profile */ ++ newprofile = sd_profilelist_find(filename); ++ if (newprofile) { ++ SD_DEBUG("%s: setting profile %s\n", ++ __FUNCTION__, newprofile->name); ++ } else if (findprofile_mandatory) { ++ /* Profile (mandatory) could not be found */ ++ ++ if (complain) { ++ LOG_HINT(sd, GFP_KERNEL, HINT_MANDPROF, ++ "image=%s pid=%d profile=%s active=%s\n", ++ filename, ++ current->pid, ++ sd->profile->name, ++ sd->active->name); ++ ++ newprofile = get_sdprofile(null_complain_profile); ++ } else { ++ SD_WARN("REJECTING exec(2) of image '%s'. " ++ "Profile mandatory and not found " ++ "(%s(%d) profile %s active %s)\n", ++ filename, ++ current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ error = -EPERM; ++ } ++ } else { ++ /* Profile (non-mandatory) could not be found */ ++ ++ /* Only way we can get into this code is if task ++ * is unconstrained. ++ */ ++ ++ BUG_ON(__sd_is_confined(sd)); ++ ++ SD_DEBUG("%s: No profile found for exec image %s\n", ++ __FUNCTION__, ++ filename); ++ } /* newprofile */ ++ ++ ++apply_profile: ++ /* Apply profile if necessary */ ++ if (newprofile) { ++ struct subdomain *latest_sd; ++ unsigned long flags; ++ ++ if (newprofile == &unconstrained_flag) ++ newprofile = NULL; ++ ++ /* grab a write lock ++ * ++ * Several things may have changed since the code above ++ * ++ * - If we are a confined process, sd is a refcounted copy of ++ * the subdomain (get_sdcopy) and not the actual subdomain. ++ * This allows us to not have to hold a read lock around ++ * all this code. However, we need to change the actual ++ * subdomain, not the copy. Also, if profile replacement ++ * has taken place, our sd->profile may be inaccurate ++ * so we need to undo the copy and reverse the refcounting. ++ * ++ * - If newprofile points to an actual profile (result of ++ * sd_profilelist_find above), this profile may have been ++ * replaced. We need to fix it up. Doing this to avoid ++ * having to hold a write lock around all this code. ++ */ ++ ++ write_lock_irqsave(&sd_lock, flags); ++ ++ /* task is guaranteed to have a subdomain (->security) ++ * by this point ++ */ ++ latest_sd = SD_SUBDOMAIN(current->security); ++ ++ /* Determine if profile we found earlier is stale. ++ * If so, reobtain it. N.B stale flag should never be ++ * set on null_complain profile. ++ */ ++ if (newprofile && unlikely(newprofile->isstale)) { ++ BUG_ON(newprofile == null_complain_profile); ++ ++ /* drop refcnt obtained from earlier get_sdprofile */ ++ put_sdprofile(newprofile); ++ ++ newprofile = sd_profilelist_find(filename); ++ ++ if (!newprofile) { ++ /* Race, profile was removed, not replaced. ++ * Redo with error checking ++ */ ++ write_unlock_irqrestore(&sd_lock, flags); ++ goto find_profile; ++ } ++ } ++ ++ /* need to drop reference counts we obtained in get_sdcopy ++ * above. Need to do it before overwriting latest_sd, in ++ * case latest_sd == sd (no async replacement has taken place). ++ */ ++ if (issdcopy) { ++ put_sdcopy(sd); ++ issdcopy = 0; ++ } ++ ++ sd_switch(latest_sd, newprofile, newprofile); ++ put_sdprofile(newprofile); ++ ++ if (complain && newprofile == null_complain_profile) ++ LOG_HINT(latest_sd, GFP_ATOMIC, HINT_CHGPROF, ++ "pid=%d\n", ++ current->pid); ++ ++ write_unlock_irqrestore(&sd_lock, flags); ++ } ++ ++ sd_put_name(filename); ++ ++ if (issdcopy) ++ put_sdcopy(sd); ++ ++out: ++ return error; ++} ++ ++/** ++ * sd_release - release the task's subdomain ++ * @p: task being released ++ * ++ * This is called after a task has exited and the parent has reaped it. ++ * @p->security blob is freed. ++ */ ++void sd_release(struct task_struct *p) ++{ ++ struct subdomain *sd = SD_SUBDOMAIN(p->security); ++ if (sd) { ++ p->security = NULL; ++ ++ sd_subdomainlist_remove(sd); ++ ++ /* release profiles */ ++ put_sdprofile(sd->profile); ++ put_sdprofile(sd->active); ++ ++ kfree(sd); ++ } ++} ++ ++/***************************** ++ * GLOBAL SUBPROFILE FUNCTIONS ++ ****************************/ ++ ++/** ++ * do_change_hat - actually switch hats ++ * @name: name of hat to swtich to ++ * @sd: current subdomain ++ * ++ * Switch to a new hat. Return 0 on success, error otherwise. ++ */ ++static inline int do_change_hat(const char *hat_name, struct subdomain *sd) ++{ ++ struct sdprofile *sub; ++ struct sdprofile *p = sd->active; ++ int error = 0; ++ ++ sub = __sd_find_profile(hat_name, &sd->profile->sub); ++ ++ if (sub) { ++ /* change hat */ ++ sd->active = sub; ++ } else { ++ /* There is no such subprofile change to a NULL profile. ++ * The NULL profile grants no file access. ++ * ++ * This feature is used by changehat_apache. ++ * ++ * N.B from the null-profile the task can still changehat back ++ * out to the parent profile (assuming magic != NULL) ++ */ ++ if (SUBDOMAIN_COMPLAIN(sd)) { ++ LOG_HINT(sd, GFP_ATOMIC, HINT_UNKNOWN_HAT, ++ "%s pid=%d " ++ "profile=%s active=%s\n", ++ hat_name, ++ current->pid, ++ sd->profile->name, ++ sd->active->name); ++ sd->active = get_sdprofile(null_complain_profile); ++ } else { ++ SD_DEBUG("%s: Unknown hatname '%s'. " ++ "Changing to NULL profile " ++ "(%s(%d) profile %s active %s)\n", ++ __FUNCTION__, ++ hat_name, ++ current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ ++ sd->active = get_sdprofile(null_profile); ++ error = -EACCES; ++ } ++ } ++ put_sdprofile(p); ++ ++ return error; ++} ++ ++/** ++ * sd_change_hat - change hat to/from subprofile ++ * @hat_name: specifies hat to change to ++ * @hat_magic: token to validate hat change ++ * ++ * Change to new @hat_name when current hat is top level profile, and store ++ * the @hat_magic in the current subdomain. If the new @hat_name is ++ * NULL, and the @hat_magic matches that stored in the current subdomain ++ * return to original top level profile. Returns 0 on success, error ++ * otherwise. ++ */ ++#define IN_SUBPROFILE(sd) ((sd)->profile != (sd)->active) ++int sd_change_hat(const char *hat_name, __u32 hat_magic) ++{ ++ struct subdomain *sd = SD_SUBDOMAIN(current->security); ++ int error = 0; ++ ++ SD_DEBUG("%s: %p, 0x%x (pid %d)\n", ++ __FUNCTION__, ++ hat_name, hat_magic, ++ current->pid); ++ ++ /* Dump out above debugging in WARN mode if we are in AUDIT mode */ ++ if (SUBDOMAIN_AUDIT(sd)) { ++ SD_WARN("%s: %s, 0x%x (pid %d)\n", ++ __FUNCTION__, hat_name ? hat_name : "NULL", ++ hat_magic, current->pid); ++ } ++ ++ /* no subdomain: changehat into the null_profile, since the process ++ has no subdomain do_change_hat won't find a match which will cause ++ a changehat to null_profile. We could short circuit this but since ++ the subdprofile (hat) list is empty we would save very little. */ ++ ++ /* check to see if an unconfined process is doing a changehat. */ ++ if (!__sd_is_confined(sd)) { ++ error = -EACCES; ++ goto out; ++ } ++ ++ /* Check whether current domain is parent ++ * or one of the sibling children ++ */ ++ if (sd->profile == sd->active) { ++ /* ++ * parent ++ */ ++ if (hat_name) { ++ SD_DEBUG("%s: switching to %s, 0x%x\n", ++ __FUNCTION__, ++ hat_name, ++ hat_magic); ++ ++ /* ++ * N.B hat_magic == 0 has a special meaning ++ * this indicates that the task may never changehat ++ * back to it's parent, it will stay in this subhat ++ * (or null-profile, if the hat doesn't exist) until ++ * the task terminates ++ */ ++ sd->sd_hat_magic = hat_magic; ++ error = do_change_hat(hat_name, sd); ++ } else { ++ /* Got here via changehat(NULL, magic) ++ * ++ * We used to simply update the magic cookie. ++ * That's an odd behaviour, so just do nothing. ++ */ ++ } ++ } else { ++ /* ++ * child -- check to make sure magic is same as what was ++ * passed when we switched into this profile, ++ * Handle special casing of NULL magic which confines task ++ * to subprofile and prohibits further changehats ++ */ ++ if (hat_magic == sd->sd_hat_magic && sd->sd_hat_magic) { ++ if (!hat_name) { ++ /* ++ * Got here via changehat(NULL, magic) ++ * Return from subprofile, back to parent ++ */ ++ put_sdprofile(sd->active); ++ sd->active = get_sdprofile(sd->profile); ++ ++ /* Reset hat_magic to zero. ++ * New value will be passed on next changehat ++ */ ++ sd->sd_hat_magic = 0; ++ } else { ++ /* change to another (sibling) profile */ ++ error = do_change_hat(hat_name, sd); ++ } ++ } else if (sd->sd_hat_magic) { ++ SD_ERROR("KILLING process %s(%d) " ++ "Invalid change_hat() magic# 0x%x " ++ "(hatname %s profile %s active %s)\n", ++ current->comm, current->pid, ++ hat_magic, ++ hat_name ? hat_name : "NULL", ++ sd->profile->name, sd->active->name); ++ ++ /* terminate current process */ ++ (void)send_sig_info(SIGKILL, NULL, current); ++ } else { /* sd->sd_hat_magic == NULL */ ++ SD_ERROR("KILLING process %s(%d) " ++ "Task was confined to current subprofile " ++ "(profile %s active %s)\n", ++ current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ ++ /* terminate current process */ ++ (void)send_sig_info(SIGKILL, NULL, current); ++ } ++ ++ } ++ ++out: ++ return error; ++} +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/module_interface.c +@@ -0,0 +1,720 @@ ++/* ++ * Copyright (C) 1998-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor userspace policy interface ++ */ ++ ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++#include "module_interface.h" ++#include "aamatch/match.h" ++ ++/* sd_code defined in module_interface.h */ ++ ++const int sdcode_datasize[] = { 1, 2, 4, 8, 2, 2, 4, 0, 0, 0, 0, 0, 0 }; ++ ++struct sd_taskreplace_data { ++ struct sdprofile *old_profile; ++ struct sdprofile *new_profile; ++}; ++ ++/* inlines must be forward of there use in newer version of gcc, ++ just forward declaring with a prototype won't work anymore */ ++ ++static inline void free_sd_entry(struct sd_entry *entry) ++{ ++ if (entry) { ++ kfree(entry->filename); ++ sdmatch_free(entry->extradata); ++ kfree(entry); ++ } ++} ++ ++/** ++ * alloc_sd_entry - create new empty sd_entry ++ * ++ * This routine allocates, initializes, and returns a new subdomain ++ * file entry structure. Structure is zeroed. Returns new structure on ++ * success, NULL on failure. ++ */ ++static inline struct sd_entry *alloc_sd_entry(void) ++{ ++ struct sd_entry *entry; ++ ++ SD_DEBUG("%s\n", __FUNCTION__); ++ entry = kmalloc(sizeof(struct sd_entry), GFP_KERNEL); ++ if (entry) { ++ int i; ++ memset(entry, 0, sizeof(struct sd_entry)); ++ INIT_LIST_HEAD(&entry->list); ++ for (i = 0; i <= POS_SD_FILE_MAX; i++) { ++ INIT_LIST_HEAD(&entry->listp[i]); ++ } ++ } ++ return entry; ++} ++ ++/** ++ * free_sdprofile - free sdprofile structure ++ */ ++void free_sdprofile(struct sdprofile *profile) ++{ ++ struct sd_entry *sdent, *tmp; ++ struct sdprofile *p, *ptmp; ++ ++ SD_DEBUG("%s(%p)\n", __FUNCTION__, profile); ++ ++ if (!profile) ++ return; ++ ++ /* profile is still on global profile list -- invalid */ ++ if (!list_empty(&profile->list)) { ++ SD_ERROR("%s: internal error, " ++ "profile '%s' still on global list\n", ++ __FUNCTION__, ++ profile->name); ++ BUG(); ++ } ++ ++ list_for_each_entry_safe(sdent, tmp, &profile->file_entry, list) { ++ if (sdent->filename) ++ SD_DEBUG("freeing sd_entry: %p %s\n", ++ sdent->filename, sdent->filename); ++ list_del_init(&sdent->list); ++ free_sd_entry(sdent); ++ } ++ ++ list_for_each_entry_safe(p, ptmp, &profile->sub, list) { ++ list_del_init(&p->list); ++ put_sdprofile(p); ++ } ++ ++ if (profile->name) { ++ SD_DEBUG("%s: %s\n", __FUNCTION__, profile->name); ++ kfree(profile->name); ++ } ++ ++ kfree(profile); ++} ++ ++/** task_remove ++ * ++ * remove profile in a task's subdomain leaving the task unconfined ++ * ++ * @sd: task's subdomain ++ */ ++static inline void task_remove(struct subdomain *sd) ++{ ++ /* write_lock(&sd_lock) held here */ ++ SD_DEBUG("%s: removing profile from task %s(%d) profile %s active %s\n", ++ __FUNCTION__, ++ sd->task->comm, ++ sd->task->pid, ++ sd->profile->name, ++ sd->active->name); ++ ++ sd_switch_unconfined(sd); ++} ++ ++/** taskremove_iter ++ * ++ * Iterate over all subdomains. ++ * ++ * If any matches old_profile, then call task_remove to remove it. ++ * This leaves the task (subdomain) unconfined. ++ */ ++static int taskremove_iter(struct subdomain *sd, void *cookie) ++{ ++ struct sdprofile *old_profile = (struct sdprofile *)cookie; ++ int remove = 0; ++ unsigned long flags; ++ ++ write_lock_irqsave(&sd_lock, flags); ++ ++ if (__sd_is_confined(sd) && sd->profile == old_profile) { ++ remove = 1; /* remove item from list */ ++ task_remove(sd); ++ } ++ ++ write_unlock_irqrestore(&sd_lock, flags); ++ ++ return remove; ++} ++ ++/** task_replace ++ * ++ * replace profile in a task's subdomain with newly loaded profile ++ * ++ * @sd: task's subdomain ++ * @new: old profile ++ */ ++static inline void task_replace(struct subdomain *sd, struct sdprofile *new) ++{ ++ struct sdprofile *nactive = NULL; ++ ++ SD_DEBUG("%s: replacing profile for task %s(%d) " ++ "profile=%s (%p) active=%s (%p)\n", ++ __FUNCTION__, ++ sd->task->comm, sd->task->pid, ++ sd->profile->name, sd->profile, ++ sd->active->name, sd->active); ++ ++ if (sd->profile == sd->active) ++ nactive = get_sdprofile(new); ++ else if (sd->active) { ++ /* old in hat, new profile has hats */ ++ nactive = __sd_find_profile(sd->active->name, &new->sub); ++ ++ if (!nactive) { ++ if (new->flags.complain) ++ nactive = get_sdprofile(null_complain_profile); ++ else ++ nactive = get_sdprofile(null_profile); ++ } ++ } ++ sd_switch(sd, new, nactive); ++ ++ put_sdprofile(nactive); ++} ++ ++/** taskreplace_iter ++ * ++ * Iterate over all subdomains. ++ * ++ * If any matches old_profile, then call task_replace to replace with ++ * new_profile ++ */ ++static int taskreplace_iter(struct subdomain *sd, void *cookie) ++{ ++ struct sd_taskreplace_data *data = (struct sd_taskreplace_data *)cookie; ++ unsigned long flags; ++ ++ write_lock_irqsave(&sd_lock, flags); ++ ++ if (__sd_is_confined(sd) && sd->profile == data->old_profile) ++ task_replace(sd, data->new_profile); ++ ++ write_unlock_irqrestore(&sd_lock, flags); ++ ++ return 0; ++} ++ ++static inline int sd_inbounds(struct sd_ext *e, size_t size) ++{ ++ return (e->pos + size <= e->end); ++} ++ ++/** ++ * sdconvert - for codes that have a trailing value, convert that value ++ * and put it in dest. ++ * if a code does not have a trailing value nop ++ * @code: type code ++ * @dest: pointer to object to receive the converted value ++ * @src: pointer to value to convert ++ */ ++static void sdconvert(enum sd_code code, void *dest, void *src) ++{ ++ switch (code) { ++ case SD_U8: ++ *(u8 *)dest = *(u8 *) src; ++ break; ++ case SD_U16: ++ case SD_NAME: ++ case SD_DYN_STRING: ++ *(u16 *)dest = le16_to_cpu(get_unaligned((u16 *)src)); ++ break; ++ case SD_U32: ++ case SD_STATIC_BLOB: ++ *(u32 *)dest = le32_to_cpu(get_unaligned((u32 *)src)); ++ break; ++ case SD_U64: ++ *(u64 *)dest = le64_to_cpu(get_unaligned((u64 *)src)); ++ break; ++ default: ++ /* nop - all other type codes do not have a trailing value */ ++ ; ++ } ++} ++ ++/** ++ * sd_is_X - check if the next element is of type X and if it is within ++ * bounds. If it is put the associated value in data. ++ * @e: extent information ++ * @code: type code ++ * @data: object located at @e->pos (of type @code) is written into @data ++ * if @data is non-null. if data is null it means skip this ++ * entry ++ * return the size of bytes associated with the returned data ++ * for complex object like blob and string a pointer to the allocated ++ * data is returned in data, but the size of the blob or string is ++ * returned. ++ */ ++static u32 sd_is_X(struct sd_ext *e, enum sd_code code, void *data) ++{ ++ void *pos = e->pos; ++ int ret = 0; ++ if (!sd_inbounds(e, SD_CODE_BYTE + sdcode_datasize[code])) ++ goto fail; ++ if (code != *(u8 *)e->pos) ++ goto out; ++ e->pos += SD_CODE_BYTE; ++ if (code == SD_NAME) { ++ u16 size; ++ /* name codes are followed by X bytes */ ++ size = le16_to_cpu(get_unaligned((u16 *)e->pos)); ++ if (!sd_inbounds(e, (size_t) size)) ++ goto fail; ++ if (data) ++ *(u16 *)data = size; ++ e->pos += sdcode_datasize[code]; ++ ret = 1 + sdcode_datasize[code]; ++ } else if (code == SD_DYN_STRING) { ++ u16 size; ++ char *str; ++ /* strings codes are followed by X bytes */ ++ size = le16_to_cpu(get_unaligned((u16 *)e->pos)); ++ e->pos += sdcode_datasize[code]; ++ if (!sd_inbounds(e, (size_t) size)) ++ goto fail; ++ if (data) { ++ * (char **)data = NULL; ++ str = kmalloc(size, GFP_KERNEL); ++ if (!str) ++ goto fail; ++ memcpy(str, e->pos, (size_t) size); ++ str[size-1] = '\0'; ++ * (char **)data = str; ++ } ++ e->pos += size; ++ ret = size; ++ } else if (code == SD_STATIC_BLOB) { ++ u32 size; ++ /* blobs are followed by X bytes, that can be 2^32 */ ++ size = le32_to_cpu(get_unaligned((u32 *)e->pos)); ++ e->pos += sdcode_datasize[code]; ++ if (!sd_inbounds(e, (size_t) size)) ++ goto fail; ++ if (data) ++ memcpy(data, e->pos, (size_t) size); ++ e->pos += size; ++ ret = size; ++ } else { ++ if (data) ++ sdconvert(code, data, e->pos); ++ e->pos += sdcode_datasize[code]; ++ ret = 1 + sdcode_datasize[code]; ++ } ++out: ++ return ret; ++fail: ++ e->pos = pos; ++ return 0; ++} ++ ++/* sd_is_nameX - check is the next element is X, and its tag is name. ++ * if the code matches and name (if specified) matches then the packed data ++ * is unpacked into *data. (Note for strings this is the size, and the next ++ * data in the stream is the string data) ++ * returns 0 if either match failes ++ */ ++static int sd_is_nameX(struct sd_ext *e, enum sd_code code, void *data, ++ const char *name) ++{ ++ void *pos = e->pos; ++ u16 size; ++ u32 ret; ++ /* check for presence of a tagname, and if present name size ++ * SD_NAME tag value is a u16 */ ++ if (sd_is_X(e, SD_NAME, &size)) { ++ /* if a name is specified it must match. otherwise skip tag */ ++ if (name && ((strlen(name) != size-1) || ++ strncmp(name, (char *)e->pos, (size_t)size-1))) ++ goto fail; ++ e->pos += size; ++ } ++ /* now check if data actually matches */ ++ ret = sd_is_X(e, code, data); ++ if (!ret) ++ goto fail; ++ return ret; ++ ++fail: ++ e->pos = pos; ++ return 0; ++} ++ ++/* macro to wrap error case to make a block of reads look nicer */ ++#define SD_READ_X(E, C, D, N) \ ++ do { \ ++ u32 __ret; \ ++ __ret = sd_is_nameX((E), (C), (D), (N)); \ ++ if (!__ret) \ ++ goto fail; \ ++ } while (0) ++ ++/** ++ * sd_activate_net_entry - ignores/skips net entries if the they are present ++ * in the data stream. ++ * @e: extent information ++ */ ++static inline int sd_activate_net_entry(struct sd_ext *e) ++{ ++ SD_READ_X(e, SD_STRUCT, NULL, "ne"); ++ SD_READ_X(e, SD_U32, NULL, NULL); ++ SD_READ_X(e, SD_U32, NULL, NULL); ++ SD_READ_X(e, SD_U32, NULL, NULL); ++ SD_READ_X(e, SD_U16, NULL, NULL); ++ SD_READ_X(e, SD_U16, NULL, NULL); ++ SD_READ_X(e, SD_U32, NULL, NULL); ++ SD_READ_X(e, SD_U32, NULL, NULL); ++ SD_READ_X(e, SD_U16, NULL, NULL); ++ SD_READ_X(e, SD_U16, NULL, NULL); ++ /* interface name is optional so just ignore return code */ ++ sd_is_nameX(e, SD_DYN_STRING, NULL, NULL); ++ SD_READ_X(e, SD_STRUCTEND, NULL, NULL); ++ ++ return 1; ++fail: ++ return 0; ++} ++ ++static inline struct sd_entry *sd_activate_file_entry(struct sd_ext *e) ++{ ++ struct sd_entry *entry = NULL; ++ ++ if (!(entry = alloc_sd_entry())) ++ goto fail; ++ ++ SD_READ_X(e, SD_STRUCT, NULL, "fe"); ++ SD_READ_X(e, SD_DYN_STRING, &entry->filename, NULL); ++ SD_READ_X(e, SD_U32, &entry->mode, "file.mode"); ++ SD_READ_X(e, SD_U32, &entry->entry_type, "file.pattern_type"); ++ ++ entry->extradata = sdmatch_alloc(entry->entry_type); ++ if (IS_ERR(entry->extradata)) { ++ entry->extradata = NULL; ++ goto fail; ++ } ++ ++ if (entry->extradata && ++ sdmatch_serialize(entry->extradata, e, sd_is_nameX) != 0) { ++ goto fail; ++ } ++ SD_READ_X(e, SD_STRUCTEND, NULL, NULL); ++ ++ switch (entry->entry_type) { ++ case sd_entry_literal: ++ SD_DEBUG("%s: %s [no pattern] mode=0x%x\n", ++ __FUNCTION__, ++ entry->filename, ++ entry->mode); ++ break; ++ case sd_entry_tailglob: ++ SD_DEBUG("%s: %s [tailglob] mode=0x%x\n", ++ __FUNCTION__, ++ entry->filename, ++ entry->mode); ++ break; ++ case sd_entry_pattern: ++ SD_DEBUG("%s: %s mode=0x%x\n", ++ __FUNCTION__, ++ entry->filename, ++ entry->mode); ++ break; ++ default: ++ SD_WARN("%s: INVALID entry_type %d\n", ++ __FUNCTION__, ++ (int)entry->entry_type); ++ goto fail; ++ } ++ ++ return entry; ++ ++fail: ++ sdmatch_free(entry->extradata); ++ free_sd_entry(entry); ++ return NULL; ++} ++ ++static inline int check_rule_and_add(struct sd_entry *file_entry, ++ struct sdprofile *profile, ++ const char **message) ++{ ++ /* verify consistency of x, px, ix, ux for entry against ++ possible duplicates for this entry */ ++ int mode = SD_EXEC_MODIFIER_MASK(file_entry->mode); ++ int i; ++ ++ if (mode && !(SD_MAY_EXEC & file_entry->mode)) { ++ *message = "inconsistent rule, x modifiers without x"; ++ goto out; ++ } ++ ++ /* check that only 1 of the modifiers is set */ ++ if (mode && (mode & (mode - 1))) { ++ *message = "inconsistent rule, multiple x modifiers"; ++ goto out; ++ } ++ ++ list_add(&file_entry->list, &profile->file_entry); ++ profile->num_file_entries++; ++ ++ mode = file_entry->mode; ++ ++ /* Handle partitioned lists ++ * Chain entries onto sublists based on individual ++ * permission bits. This allows more rapid searching. ++ */ ++ for (i = 0; i <= POS_SD_FILE_MAX; i++) { ++ if (mode & (1 << i)) ++ /* profile->file_entryp[i] initially set to ++ * NULL in alloc_sdprofile() */ ++ list_add(&file_entry->listp[i], ++ &profile->file_entryp[i]); ++ } ++ ++ return 1; ++ ++out: ++ free_sd_entry(file_entry); ++ return 0; ++} ++ ++#define SD_ENTRY_LIST(NAME) \ ++ do { \ ++ if (sd_is_nameX(e, SD_LIST, NULL, (NAME))) { \ ++ rulename = ""; \ ++ error_string = "Invalid file entry"; \ ++ while (!sd_is_nameX(e, SD_LISTEND, NULL, NULL)) { \ ++ struct sd_entry *file_entry; \ ++ file_entry = sd_activate_file_entry(e); \ ++ if (!file_entry) \ ++ goto fail; \ ++ if (!check_rule_and_add(file_entry, profile, \ ++ &error_string)) { \ ++ rulename = file_entry->filename; \ ++ goto fail; \ ++ } \ ++ } \ ++ } \ ++ } while (0) ++ ++struct sdprofile *sd_activate_profile(struct sd_ext *e, ssize_t *error) ++{ ++ struct sdprofile *profile = NULL; ++ const char *rulename = ""; ++ const char *error_string = "Invalid Profile"; ++ ++ *error = -EPROTO; ++ ++ profile = alloc_sdprofile(); ++ if (!profile) { ++ error_string = "Could not allocate profile"; ++ *error = -ENOMEM; ++ goto fail; ++ } ++ ++ /* check that we have the right struct being passed */ ++ SD_READ_X(e, SD_STRUCT, NULL, "profile"); ++ SD_READ_X(e, SD_DYN_STRING, &profile->name, NULL); ++ ++ error_string = "Invalid flags"; ++ /* per profile debug flags (debug, complain, audit) */ ++ SD_READ_X(e, SD_STRUCT, NULL, "flags"); ++ SD_READ_X(e, SD_U32, &(profile->flags.debug), "profile.flags.debug"); ++ SD_READ_X(e, SD_U32, &(profile->flags.complain), ++ "profile.flags.complain"); ++ SD_READ_X(e, SD_U32, &(profile->flags.audit), "profile.flags.audit"); ++ SD_READ_X(e, SD_STRUCTEND, NULL, NULL); ++ ++ error_string = "Invalid capabilities"; ++ SD_READ_X(e, SD_U32, &(profile->capabilities), "profile.capabilities"); ++ ++ /* get the file entries. */ ++ SD_ENTRY_LIST("pgent"); /* pcre rules */ ++ SD_ENTRY_LIST("sgent"); /* simple globs */ ++ SD_ENTRY_LIST("fent"); /* regular file entries */ ++ ++ /* get the net entries */ ++ if (sd_is_nameX(e, SD_LIST, NULL, "net")) { ++ error_string = "Invalid net entry"; ++ while (!sd_is_nameX(e, SD_LISTEND, NULL, NULL)) { ++ if (!sd_activate_net_entry(e)) ++ goto fail; ++ } ++ } ++ rulename = ""; ++ ++ /* get subprofiles */ ++ if (sd_is_nameX(e, SD_LIST, NULL, "hats")) { ++ error_string = "Invalid profile hat"; ++ while (!sd_is_nameX(e, SD_LISTEND, NULL, NULL)) { ++ struct sdprofile *subprofile; ++ subprofile = sd_activate_profile(e, error); ++ if (!subprofile) ++ goto fail; ++ get_sdprofile(subprofile); ++ list_add(&subprofile->list, &profile->sub); ++ } ++ } ++ ++ error_string = "Invalid end of profile"; ++ SD_READ_X(e, SD_STRUCTEND, NULL, NULL); ++ ++ return profile; ++ ++fail: ++ SD_WARN("%s: %s %s in profile %s\n", INTERFACE_ID, rulename, ++ error_string, profile && profile->name ? profile->name ++ : "unknown"); ++ ++ if (profile) { ++ free_sdprofile(profile); ++ profile = NULL; ++ } ++ ++ return NULL; ++} ++ ++void *sd_activate_top_profile(struct sd_ext *e, ssize_t *error) ++{ ++ /* get the interface version */ ++ if (!sd_is_nameX(e, SD_U32, &e->version, "version")) { ++ SD_WARN("%s: version missing\n", INTERFACE_ID); ++ *error = -EPROTONOSUPPORT; ++ goto out; ++ } ++ ++ /* check that the interface version is currently supported */ ++ if (e->version != 2) { ++ SD_WARN("%s: unsupported interface version (%d)\n", ++ INTERFACE_ID, e->version); ++ *error = -EPROTONOSUPPORT; ++ goto out; ++ } ++ ++ return sd_activate_profile(e, error); ++out: ++ return NULL; ++} ++ ++ssize_t sd_file_prof_add(void *data, size_t size) ++{ ++ struct sdprofile *profile = NULL; ++ ++ struct sd_ext e = { data, data + size, data }; ++ ssize_t error; ++ ++ profile = sd_activate_top_profile(&e, &error); ++ if (!profile) { ++ SD_DEBUG("couldn't activate profile\n"); ++ return error; ++ } ++ ++ if (!sd_profilelist_add(profile)) { ++ SD_WARN("trying to add profile (%s) that already exists.\n", ++ profile->name); ++ free_sdprofile(profile); ++ return -EEXIST; ++ } ++ ++ return size; ++} ++ ++ssize_t sd_file_prof_repl(void *udata, size_t size) ++{ ++ struct sd_taskreplace_data data; ++ struct sd_ext e = { udata, udata + size, udata }; ++ ssize_t error; ++ ++ data.new_profile = sd_activate_top_profile(&e, &error); ++ if (!data.new_profile) { ++ SD_DEBUG("couldn't activate profile\n"); ++ return error; ++ } ++ /* Grab reference to close race window (see comment below) */ ++ get_sdprofile(data.new_profile); ++ ++ /* Replace the profile on the global profile list. ++ * This list is used by all new exec's to find the correct profile. ++ * If there was a previous profile, it is returned, else NULL. ++ * ++ * N.B sd_profilelist_replace does not drop the refcnt on ++ * old_profile when removing it from the global list, otherwise it ++ * could reach zero and be automatically free'd. We nust manually ++ * drop it at the end of this function when we are finished with it. ++ */ ++ data.old_profile = sd_profilelist_replace(data.new_profile); ++ ++ /* RACE window here. ++ * At this point another task could preempt us trying to replace ++ * the SAME profile. If it makes it to this point, it has removed ++ * the original tasks new_profile from the global list and holds a ++ * reference of 1 to it in it's old_profile. If the new task ++ * reaches the end of the function it will put old_profile causing ++ * the profile to be deleted. ++ * When the original task is rescheduled it will continue calling ++ * sd_subdomainlist_iterate relabelling tasks with a profile ++ * which points to free'd memory. ++ */ ++ ++ /* If there was an old profile, find all currently executing tasks ++ * using this profile and replace the old profile with the new. ++ */ ++ if (data.old_profile) { ++ SD_DEBUG("%s: try to replace profile (%p)%s\n", ++ __FUNCTION__, ++ data.old_profile, ++ data.old_profile->name); ++ ++ sd_subdomainlist_iterate(taskreplace_iter, (void *)&data); ++ ++ /* mark old profile as stale */ ++ data.old_profile->isstale = 1; ++ ++ /* it's off global list, and we are done replacing */ ++ put_sdprofile(data.old_profile); ++ } ++ ++ /* Free reference obtained above */ ++ put_sdprofile(data.new_profile); ++ ++ return size; ++} ++ ++ssize_t sd_file_prof_remove(const char *name, size_t size) ++{ ++ struct sdprofile *old_profile; ++ ++ /* Do this step to get a guaranteed reference to profile ++ * as sd_profilelist_remove may drop it to zero which would ++ * made subsequent attempt to iterate using it unsafe ++ */ ++ old_profile = sd_profilelist_find(name); ++ ++ if (old_profile) { ++ if (sd_profilelist_remove(name) != 0) ++ SD_WARN("%s: race trying to remove profile (%s)\n", ++ __FUNCTION__, name); ++ ++ /* remove profile from any tasks using it */ ++ sd_subdomainlist_iterateremove(taskremove_iter, ++ (void *)old_profile); ++ ++ /* drop reference obtained by sd_profilelist_find */ ++ put_sdprofile(old_profile); ++ } else { ++ SD_WARN("%s: trying to remove profile (%s) that " ++ "doesn't exist - skipping.\n", __FUNCTION__, name); ++ return -ENOENT; ++ } ++ ++ return size; ++} +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/module_interface.h +@@ -0,0 +1,37 @@ ++#ifndef __MODULEINTERFACE_H ++#define __MODULEINTERFACE_H ++ ++/* Codes of the types of basic structures that are understood */ ++#define SD_CODE_BYTE (sizeof(u8)) ++#define INTERFACE_ID "INTERFACE" ++ ++#define SUBDOMAIN_INTERFACE_VERSION 2 ++ ++enum sd_code { ++ SD_U8, ++ SD_U16, ++ SD_U32, ++ SD_U64, ++ SD_NAME, /* same as string except it is items name */ ++ SD_DYN_STRING, ++ SD_STATIC_BLOB, ++ SD_STRUCT, ++ SD_STRUCTEND, ++ SD_LIST, ++ SD_LISTEND, ++ SD_OFFSET, ++ SD_BAD ++}; ++ ++/* sd_ext tracks the kernel buffer and read position in it. The interface ++ * data is copied into a kernel buffer in subdomainfs and then handed off to ++ * the activate routines. ++ */ ++struct sd_ext { ++ void *start; ++ void *end; ++ void *pos; /* pointer to current position in the buffer */ ++ u32 version; ++}; ++ ++#endif /* __MODULEINTERFACE_H */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/procattr.c +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor /proc/pid/attr handling ++ */ ++ ++/* for isspace */ ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++size_t sd_getprocattr(struct subdomain *sd, char *str, size_t size) ++{ ++ int error = -EACCES; /* default to a perm denied */ ++ size_t len; ++ ++ if (__sd_is_confined(sd)) { ++ size_t lena, lenm, lenp = 0; ++ const char *enforce_str = " (enforce)"; ++ const char *complain_str = " (complain)"; ++ const char *mode_str = ++ SUBDOMAIN_COMPLAIN(sd) ? complain_str : enforce_str; ++ ++ lenm = strlen(mode_str); ++ ++ lena = strlen(sd->active->name); ++ ++ len = lena; ++ if (sd->active != sd->profile) { ++ lenp = strlen(sd->profile->name); ++ len += (lenp + 1); /* +1 for ^ */ ++ } ++ /* DONT null terminate strings we output via proc */ ++ len += (lenm + 1); /* for \n */ ++ ++ if (len <= size) { ++ if (lenp) { ++ memcpy(str, sd->profile->name, lenp); ++ str += lenp; ++ *str++ = '^'; ++ } ++ ++ memcpy(str, sd->active->name, lena); ++ str += lena; ++ memcpy(str, mode_str, lenm); ++ str += lenm; ++ *str++ = '\n'; ++ error = len; ++ } else { ++ error = -ERANGE; ++ } ++ } else { ++ const char *unconstrained_str = SD_UNCONSTRAINED "\n"; ++ len = strlen(unconstrained_str); ++ ++ /* DONT null terminate strings we output via proc */ ++ if (len <= size) { ++ memcpy(str, unconstrained_str, len); ++ error = len; ++ } else { ++ error = -ERANGE; ++ } ++ } ++ ++ return error; ++ ++} ++int sd_setprocattr_changehat(char *hatinfo, size_t infosize) ++{ ++ int error = -EINVAL; ++ char *token = NULL, *hat, *smagic, *tmp; ++ __u32 magic; ++ int rc, len, consumed; ++ unsigned long flags; ++ ++ SD_DEBUG("%s: %p %zd\n", __FUNCTION__, hatinfo, infosize); ++ ++ /* strip leading white space */ ++ while (infosize && isspace(*hatinfo)) { ++ hatinfo++; ++ infosize--; ++ } ++ ++ if (infosize == 0) ++ goto out; ++ ++ /* ++ * Copy string to a new buffer so we can play with it ++ * It may be zero terminated but we add a trailing 0 ++ * for 100% safety ++ */ ++ token = kmalloc(infosize + 1, GFP_KERNEL); ++ ++ if (!token) { ++ error = -ENOMEM; ++ goto out; ++ } ++ ++ memcpy(token, hatinfo, infosize); ++ token[infosize] = 0; ++ ++ /* error is INVAL until we have at least parsed something */ ++ error = -EINVAL; ++ ++ tmp = token; ++ while (*tmp && *tmp != '^') { ++ tmp++; ++ } ++ ++ if (!*tmp || tmp == token) { ++ SD_WARN("%s: Invalid input '%s'\n", __FUNCTION__, token); ++ goto out; ++ } ++ ++ /* split magic and hat into two strings */ ++ *tmp = 0; ++ smagic = token; ++ ++ /* ++ * Initially set consumed=strlen(magic), as if sscanf ++ * consumes all input via the %x it will not process the %n ++ * directive. Otherwise, if sscanf does not consume all the ++ * input it will process the %n and update consumed. ++ */ ++ consumed = len = strlen(smagic); ++ ++ rc = sscanf(smagic, "%x%n", &magic, &consumed); ++ ++ if (rc != 1 || consumed != len) { ++ SD_WARN("%s: Invalid hex magic %s\n", ++ __FUNCTION__, ++ smagic); ++ goto out; ++ } ++ ++ hat = tmp + 1; ++ ++ if (!*hat) ++ hat = NULL; ++ ++ if (!hat && !magic) { ++ SD_WARN("%s: Invalid input, NULL hat and NULL magic\n", ++ __FUNCTION__); ++ goto out; ++ } ++ ++ SD_DEBUG("%s: Magic 0x%x Hat '%s'\n", ++ __FUNCTION__, magic, hat ? hat : NULL); ++ ++ write_lock_irqsave(&sd_lock, flags); ++ error = sd_change_hat(hat, magic); ++ write_unlock_irqrestore(&sd_lock, flags); ++ ++out: ++ if (token) { ++ memset(token, 0, infosize); ++ kfree(token); ++ } ++ ++ return error; ++} ++ ++int sd_setprocattr_setprofile(struct task_struct *p, char *profilename, ++ size_t profilesize) ++{ ++ int error = -EINVAL; ++ struct sdprofile *profile; ++ struct subdomain *sd; ++ char *name = NULL; ++ unsigned long flags; ++ ++ SD_DEBUG("%s: current %s(%d)\n", ++ __FUNCTION__, current->comm, current->pid); ++ ++ /* strip leading white space */ ++ while (profilesize && isspace(*profilename)) { ++ profilename++; ++ profilesize--; ++ } ++ ++ if (profilesize == 0) ++ goto out; ++ ++ /* ++ * Copy string to a new buffer so we guarantee it is zero ++ * terminated ++ */ ++ name = kmalloc(profilesize + 1, GFP_KERNEL); ++ ++ if (!name) { ++ error = -ENOMEM; ++ goto out; ++ } ++ ++ strncpy(name, profilename, profilesize); ++ name[profilesize] = 0; ++ ++ if (strcmp(name, SD_UNCONSTRAINED) == 0) ++ profile = null_profile; ++ else ++ profile = sd_profilelist_find(name); ++ ++ if (!profile) { ++ SD_WARN("%s: Unable to switch task %s(%d) to profile '%s'. " ++ "No such profile.\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ name); ++ ++ error = -EINVAL; ++ goto out; ++ } ++ ++ ++ write_lock_irqsave(&sd_lock, flags); ++ ++ sd = SD_SUBDOMAIN(p->security); ++ ++ /* switch to unconstrained */ ++ if (profile == null_profile) { ++ if (__sd_is_confined(sd)) { ++ SD_WARN("%s: Unconstraining task %s(%d) " ++ "profile %s active %s\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ sd->profile->name, ++ sd->active->name); ++ ++ sd_switch_unconfined(sd); ++ } else { ++ SD_WARN("%s: task %s(%d) " ++ "is already unconstrained\n", ++ __FUNCTION__, p->comm, p->pid); ++ } ++ } else { ++ if (!sd) { ++ /* this task was created before module was ++ * loaded, allocate a subdomain ++ */ ++ SD_WARN("%s: task %s(%d) has no subdomain\n", ++ __FUNCTION__, p->comm, p->pid); ++ ++ /* unlock so we can safely GFP_KERNEL */ ++ write_unlock_irqrestore(&sd_lock, flags); ++ ++ sd = alloc_subdomain(p); ++ if (!sd) { ++ SD_WARN("%s: Unable to allocate subdomain for " ++ "task %s(%d). Cannot confine task to " ++ "profile %s\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ name); ++ ++ error = -ENOMEM; ++ put_sdprofile(profile); ++ ++ goto out; ++ } ++ ++ write_lock_irqsave(&sd_lock, flags); ++ if (!SD_SUBDOMAIN(p->security)) { ++ p->security = sd; ++ } else { /* race */ ++ free_subdomain(sd); ++ sd = SD_SUBDOMAIN(p->security); ++ } ++ } ++ ++ /* we do not do a normal task replace since we are not ++ * replacing with the same profile. ++ * If existing process is in a hat, it will be moved ++ * into the new parent profile, even if this new ++ * profile has a identical named hat. ++ */ ++ ++ SD_WARN("%s: Switching task %s(%d) " ++ "profile %s active %s to new profile %s\n", ++ __FUNCTION__, ++ p->comm, p->pid, ++ sd->profile ? sd->profile->name : SD_UNCONSTRAINED, ++ sd->active ? sd->profile->name : SD_UNCONSTRAINED, ++ name); ++ ++ sd_switch(sd, profile, profile); ++ ++ put_sdprofile(profile); /* drop ref we obtained above ++ * from sd_profilelist_find ++ */ ++ ++ /* Reset magic in case we were in a subhat before ++ * This is the only case where we zero the magic after ++ * calling sd_switch ++ */ ++ sd->sd_hat_magic = 0; ++ } ++ ++ write_unlock_irqrestore(&sd_lock, flags); ++ ++out: ++ kfree(name); ++ ++ return error; ++} +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/aamatch/match.h +@@ -0,0 +1,137 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor submodule (match) prototypes ++ */ ++ ++#ifndef __MATCH_H ++#define __MATCH_H ++ ++#include "../module_interface.h" ++#include "../apparmor.h" ++ ++/* The following functions implement an interface used by the primary ++ * AppArmor module to perform name matching (n.b. "AppArmor" was previously ++ * called "SubDomain"). ++ ++ * sdmatch_alloc ++ * sdmatch_free ++ * sdmatch_features ++ * sdmatch_serialize ++ * sdmatch_match ++ * ++ * The intent is for the primary module to export (via virtual fs entries) ++ * the features provided by the submodule (sdmatch_features) so that the ++ * parser may only load policy that can be supported. ++ * ++ * The primary module will call sdmatch_serialize to allow the submodule ++ * to consume submodule specific data from parser data stream and will call ++ * sdmatch_match to determine if a pathname matches an sd_entry. ++ */ ++ ++typedef int (*sdmatch_serializecb) ++ (struct sd_ext *, enum sd_code, void *, const char *); ++ ++/** ++ * sdmatch_alloc: allocate extradata (if necessary) ++ * @entry_type: type of entry being allocated ++ * Return value: NULL indicates no data was allocated (ERR_PTR(x) on error) ++ */ ++extern void* sdmatch_alloc(enum entry_t entry_type); ++ ++/** ++ * sdmatch_free: release data allocated by sdmatch_alloc ++ * @entry_extradata: data previously allocated by sdmatch_alloc ++ */ ++extern void sdmatch_free(void *entry_extradata); ++ ++/** ++ * sdmatch_features: return match types supported ++ * Return value: space seperated string (of types supported - use type=value ++ * to indicate variants of a type) ++ */ ++extern const char* sdmatch_features(void); ++ ++/** ++ * sdmatch_serialize: serialize extradata ++ * @entry_extradata: data previously allocated by sdmatch_alloc ++ * @e: input stream ++ * @cb: callback fn (consume incoming data stream) ++ * Return value: 0 success, -ve error ++ */ ++extern int sdmatch_serialize(void *entry_extradata, struct sd_ext *e, ++ sdmatch_serializecb cb); ++ ++/** ++ * sdmatch_match: determine if pathname matches entry ++ * @pathname: pathname to verify ++ * @entry_name: entry name ++ * @entry_type: type of entry ++ * @entry_extradata: data previously allocated by sdmatch_alloc ++ * Return value: 1 match, 0 othersise ++ */ ++extern unsigned int sdmatch_match(const char *pathname, const char *entry_name, ++ enum entry_t entry_type, ++ void *entry_extradata); ++ ++ ++/** ++ * sd_getentry_type - return string representation of entry_t ++ * @etype: entry type ++ */ ++static inline const char *sd_getentry_type(enum entry_t etype) ++{ ++ const char *etype_names[] = { ++ "sd_entry_literal", ++ "sd_entry_tailglob", ++ "sd_entry_pattern", ++ "sd_entry_invalid" ++ }; ++ ++ if (etype >= sd_entry_invalid) { ++ etype = sd_entry_invalid; ++ } ++ ++ return etype_names[etype]; ++} ++ ++/** ++ * sdmatch_match_common - helper function to check if a pathname matches ++ * a literal/tailglob ++ * @path: path requested to search for ++ * @entry_name: name from sd_entry ++ * @etype: type of entry ++ */ ++static inline int sdmatch_match_common(const char *path, ++ const char *entry_name, ++ enum entry_t etype) ++{ ++ int retval; ++ ++ /* literal, no pattern matching characters */ ++ if (etype == sd_entry_literal) { ++ retval = (strcmp(entry_name, path) == 0); ++ /* trailing ** glob pattern */ ++ } else if (etype == sd_entry_tailglob) { ++ retval = (strncmp(entry_name, path, ++ strlen(entry_name) - 2) == 0); ++ } else { ++ SD_WARN("%s: Invalid entry_t %d\n", __FUNCTION__, etype); ++ retval = 0; ++ } ++ ++#if 0 ++ SD_DEBUG("%s(%d): %s %s [%s]\n", ++ __FUNCTION__, retval, path, entry_name, ++ sd_getentry_type(etype)); ++#endif ++ ++ return retval; ++} ++ ++#endif /* __MATCH_H */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/aamatch/match_pcre.c +@@ -0,0 +1,169 @@ ++/* ++ * Copyright (C) 2002-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * http://forge.novell.com/modules/xfmod/project/?apparmor ++ * ++ * AppArmor aamatch submodule (w/ pattern expansion). ++ * ++ * This module makes use of a slightly modified version of the PCRE ++ * library developed by Philip Hazel . See the files ++ * pcre_* in this directory. ++ */ ++ ++#include ++#include "match.h" ++#include "pcre_exec.h" ++#include "pcre_tables.h" ++ ++static const char *features="literal tailglob pattern=pcre"; ++ ++struct sdmatch_entry ++{ ++ char *pattern; ++ pcre *compiled; ++}; ++ ++void* sdmatch_alloc(enum entry_t entry_type) ++{ ++void *ptr=NULL; ++ ++ if (entry_type == sd_entry_pattern) { ++ ptr = kmalloc(sizeof(struct sdmatch_entry), GFP_KERNEL); ++ if (ptr) ++ memset(ptr, 0, sizeof(struct sdmatch_entry)); ++ else ++ ptr=ERR_PTR(-ENOMEM); ++ } else if (entry_type != sd_entry_literal && ++ entry_type != sd_entry_tailglob) { ++ ptr = ERR_PTR(-EINVAL); ++ } ++ ++ return ptr; ++} ++ ++void sdmatch_free(void *ptr) ++{ ++ if (ptr) { ++ struct sdmatch_entry *ed = (struct sdmatch_entry *) ptr; ++ kfree(ed->pattern); ++ kfree(ed->compiled); /* allocated by SD_READ_X */ ++ } ++ kfree(ptr); ++} ++ ++const char *sdmatch_features(void) ++{ ++ return features; ++} ++ ++int sdmatch_serialize(void *entry_extradata, struct sd_ext *e, ++ sdmatch_serializecb cb) ++{ ++#define SD_READ_X(E, C, D, N) \ ++ do { \ ++ if (!cb((E), (C), (D), (N))) { \ ++ error = -EINVAL; \ ++ goto done; \ ++ }\ ++ } while (0) ++ ++ int error = 0; ++ u32 size, magic, opts; ++ u8 t_char; ++ struct sdmatch_entry *ed = (struct sdmatch_entry *) entry_extradata; ++ ++ if (ed == NULL) ++ goto done; ++ ++ SD_READ_X(e, SD_DYN_STRING, &ed->pattern, NULL); ++ ++ /* size determines the real size of the pcre struct, ++ it is size_t - sizeof(pcre) on user side. ++ uschar must be the same in user and kernel space */ ++ /* check that we are processing the correct structure */ ++ SD_READ_X(e, SD_STRUCT, NULL, "pcre"); ++ SD_READ_X(e, SD_U32, &size, "pattern.size"); ++ SD_READ_X(e, SD_U32, &magic, "pattern.magic"); ++ ++ /* the allocation of pcre is delayed because it depends on the size ++ * of the pattern */ ++ ed->compiled = (pcre *) kmalloc(size + sizeof(pcre), GFP_KERNEL); ++ if (!ed->compiled) { ++ error = -ENOMEM; ++ goto done; ++ } ++ ++ memset(ed->compiled, 0, size + sizeof(pcre)); ++ ed->compiled->magic_number = magic; ++ ed->compiled->size = size + sizeof(pcre); ++ ++ SD_READ_X(e, SD_U32, &opts, "pattern.options"); ++ ed->compiled->options = opts; ++ SD_READ_X(e, SD_U16, &ed->compiled->top_bracket, "pattern.top_bracket"); ++ SD_READ_X(e, SD_U16, &ed->compiled->top_backref, "pattern.top_backref"); ++ SD_READ_X(e, SD_U8, &t_char, "pattern.first_char"); ++ ed->compiled->first_char = t_char; ++ SD_READ_X(e, SD_U8, &t_char, "pattern.req_char"); ++ ed->compiled->req_char = t_char; ++ SD_READ_X(e, SD_U8, &t_char, "pattern.code[0]"); ++ ed->compiled->code[0] = t_char; ++ ++ SD_READ_X(e, SD_STATIC_BLOB, &ed->compiled->code[1], NULL); ++ ++ SD_READ_X(e, SD_STRUCTEND, NULL, NULL); ++ ++ /* stitch in pcre patterns, it was NULLed out by parser ++ * pcre_default_tables defined in pcre_tables.h */ ++ ed->compiled->tables = pcre_default_tables; ++ ++done: ++ if (error != 0 && ed) { ++ kfree(ed->pattern); /* allocated by SD_READ_X */ ++ kfree(ed->compiled); ++ ed->pattern = NULL; ++ ed->compiled = NULL; ++ } ++ ++ return error; ++} ++ ++unsigned int sdmatch_match(const char *pathname, const char *entry_name, ++ enum entry_t entry_type, void *entry_extradata) ++{ ++ int ret; ++ ++ if (entry_type == sd_entry_pattern) { ++ int pcreret; ++ struct sdmatch_entry *ed = ++ (struct sdmatch_entry *) entry_extradata; ++ ++ pcreret = pcre_exec(ed->compiled, NULL, ++ pathname, strlen(pathname), ++ 0, 0, NULL, 0); ++ ++ ret = (pcreret >= 0); ++ ++ // XXX - this needs access to subdomain_debug, hmmm ++ //SD_DEBUG("%s(%d): %s %s %d\n", __FUNCTION__, ++ // ret, pathname, ed->pattern, pcreret); ++ } else { ++ ret = sdmatch_match_common(pathname, entry_name, entry_type); ++ } ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL_GPL(sdmatch_alloc); ++EXPORT_SYMBOL_GPL(sdmatch_free); ++EXPORT_SYMBOL_GPL(sdmatch_features); ++EXPORT_SYMBOL_GPL(sdmatch_serialize); ++EXPORT_SYMBOL_GPL(sdmatch_match); ++ ++MODULE_DESCRIPTION("AppArmor aa_match module [pcre]"); ++MODULE_AUTHOR("Tony Jones "); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/aamatch/pcre_exec.c +@@ -0,0 +1,1945 @@ ++/* ++ * This is a modified version of pcre.c containing only the code/data ++ * required to support pcre_exec() ++ */ ++ ++ ++/************************************************* ++* Perl-Compatible Regular Expressions * ++*************************************************/ ++ ++/* ++This is a library of functions to support regular expressions whose syntax ++and semantics are as close as possible to those of the Perl 5 language. See ++the file Tech.Notes for some information on the internals. ++ ++Written by: Philip Hazel ++ ++ Copyright (c) 1997-2001 University of Cambridge ++ ++----------------------------------------------------------------------------- ++Permission is granted to anyone to use this software for any purpose on any ++computer system, and to redistribute it freely, subject to the following ++restrictions: ++ ++1. This software 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. ++ ++2. The origin of this software must not be misrepresented, either by ++ explicit claim or by omission. ++ ++3. Altered versions must be plainly marked as such, and must not be ++ misrepresented as being the original software. ++ ++4. If PCRE is embedded in any software that is released under the GNU ++ General Purpose Licence (GPL), then the terms of that licence shall ++ supersede any condition above with which it is incompatible. ++----------------------------------------------------------------------------- ++*/ ++ ++ ++/* Define DEBUG to get debugging output on stdout. */ ++ ++/* #define DEBUG */ ++ ++/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef ++inline, and there are *still* stupid compilers about that don't like indented ++pre-processor statements. I suppose it's only been 10 years... */ ++ ++#ifdef DEBUG ++#define DPRINTF(p) PCRE_PRINTF p ++#else ++#define DPRINTF(p) /*nothing*/ ++#endif ++ ++/* Include the internals header, which itself includes Standard C headers plus ++the external pcre header. */ ++ ++#include "pcre_exec.h" ++ ++ ++/* ---- CODE DELETED ---- */ ++ ++ ++/* Min and max values for the common repeats; for the maxima, 0 => infinity */ ++ ++static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; ++static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; ++ ++ ++/* ---- CODE DELETED ---- */ ++ ++ ++/* Structure for building a chain of data that actually lives on the ++ * stack, for holding the values of the subject pointer at the start of each ++ * subpattern, so as to detect when an empty string has been matched by a ++ * subpattern - to break infinite loops. */ ++ ++typedef struct eptrblock { ++ struct eptrblock *prev; ++ const uschar *saved_eptr; ++} eptrblock; ++ ++/* Flag bits for the match() function */ ++ ++#define match_condassert 0x01 /* Called to check a condition assertion */ ++#define match_isgroup 0x02 /* Set if start of bracketed group */ ++ ++ ++/* ---- CODE DELETED ---- */ ++ ++ ++/************************************************* ++ * * Global variables * ++ * *************************************************/ ++ ++/* PCRE is thread-clean and doesn't use any global variables in the normal ++ * sense. However, it calls memory allocation and free functions via the two ++ * indirections below, which are can be changed by the caller, but are shared ++ * between all threads. */ ++ ++#ifdef __KERNEL__ ++static void *kern_malloc(size_t sz) ++{ ++ return kmalloc(sz, GFP_KERNEL); ++} ++void *(*pcre_malloc)(size_t) = kern_malloc; ++void (*pcre_free)(const void *) = kfree; ++#else ++void *(*pcre_malloc)(size_t) = malloc; ++void (*pcre_free)(const void *) = free; ++#endif ++ ++ ++/************************************************* ++ * * Macros and tables for character handling * ++ * *************************************************/ ++ ++/* When UTF-8 encoding is being used, a character is no longer just a single ++ * byte. The macros for character handling generate simple sequences when used in ++ * byte-mode, and more complicated ones for UTF-8 characters. */ ++ ++#ifndef SUPPORT_UTF8 ++#define GETCHARINC(c, eptr) c = *eptr++; ++#define GETCHARLEN(c, eptr, len) c = *eptr; ++#define BACKCHAR(eptr) ++#endif ++ ++/* ---- CODE DELETED ---- */ ++ ++#ifdef DEBUG ++/************************************************* ++* Debugging function to print chars * ++*************************************************/ ++ ++/* Print a sequence of chars in printable format, stopping at the end of the ++subject if the requested. ++ ++Arguments: ++ p points to characters ++ length number to print ++ is_subject TRUE if printing from within md->start_subject ++ md pointer to matching data block, if is_subject is TRUE ++ ++Returns: nothing ++*/ ++ ++static void ++pchars(const uschar *p, int length, BOOL is_subject, match_data *md) ++{ ++int c; ++if (is_subject && length > md->end_subject - p) length = md->end_subject - p; ++while (length-- > 0) ++ if (isprint(c = *(p++))) PCRE_PRINTF("%c", c); else PCRE_PRINTF("\\x%02x", c); ++} ++#endif /* DEBUG */ ++ ++/* ---- CODE DELETED ---- */ ++ ++ ++/************************************************* ++* Match a back-reference * ++*************************************************/ ++ ++/* If a back reference hasn't been set, the length that is passed is greater ++than the number of characters left in the string, so the match fails. ++ ++Arguments: ++ offset index into the offset vector ++ eptr points into the subject ++ length length to be matched ++ md points to match data block ++ ims the ims flags ++ ++Returns: TRUE if matched ++*/ ++ ++static BOOL ++match_ref(int offset, register const uschar *eptr, int length, match_data *md, ++ unsigned long int ims) ++{ ++const uschar *p = md->start_subject + md->offset_vector[offset]; ++ ++#ifdef DEBUG ++if (eptr >= md->end_subject) ++ PCRE_PRINTF("matching subject "); ++else ++ { ++ PCRE_PRINTF("matching subject "); ++ pchars(eptr, length, TRUE, md); ++ } ++PCRE_PRINTF(" against backref "); ++pchars(p, length, FALSE, md); ++PCRE_PRINTF("\n"); ++#endif ++ ++/* Always fail if not enough characters left */ ++ ++if (length > md->end_subject - eptr) return FALSE; ++ ++/* Separate the caselesss case for speed */ ++ ++if ((ims & PCRE_CASELESS) != 0) ++ { ++ while (length-- > 0) ++ if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; ++ } ++else ++ { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } ++ ++return TRUE; ++} ++ ++ ++/************************************************* ++* Match from current position * ++*************************************************/ ++ ++/* On entry ecode points to the first opcode, and eptr to the first character ++in the subject string, while eptrb holds the value of eptr at the start of the ++last bracketed group - used for breaking infinite loops matching zero-length ++strings. ++ ++Arguments: ++ eptr pointer in subject ++ ecode position in code ++ offset_top current top pointer ++ md pointer to "static" info for the match ++ ims current /i, /m, and /s options ++ eptrb pointer to chain of blocks containing eptr at start of ++ brackets - for testing for empty matches ++ flags can contain ++ match_condassert - this is an assertion condition ++ match_isgroup - this is the start of a bracketed group ++ ++Returns: TRUE if matched ++*/ ++ ++static BOOL ++match(register const uschar *eptr, register const uschar *ecode, ++ int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb, ++ int flags) ++{ ++unsigned long int original_ims = ims; /* Save for resetting on ')' */ ++eptrblock newptrb; ++ ++/* At the start of a bracketed group, add the current subject pointer to the ++stack of such pointers, to be re-instated at the end of the group when we hit ++the closing ket. When match() is called in other circumstances, we don't add to ++the stack. */ ++ ++if ((flags & match_isgroup) != 0) ++ { ++ newptrb.prev = eptrb; ++ newptrb.saved_eptr = eptr; ++ eptrb = &newptrb; ++ } ++ ++/* Now start processing the operations. */ ++ ++for (;;) ++ { ++ int op = (int)*ecode; ++ int min, max, ctype; ++ register int i; ++ register int c; ++ BOOL minimize = FALSE; ++ ++ /* Opening capturing bracket. If there is space in the offset vector, save ++ the current subject position in the working slot at the top of the vector. We ++ mustn't change the current values of the data slot, because they may be set ++ from a previous iteration of this group, and be referred to by a reference ++ inside the group. ++ ++ If the bracket fails to match, we need to restore this value and also the ++ values of the final offsets, in case they were set by a previous iteration of ++ the same bracket. ++ ++ If there isn't enough space in the offset vector, treat this as if it were a ++ non-capturing bracket. Don't worry about setting the flag for the error case ++ here; that is handled in the code for KET. */ ++ ++ if (op > OP_BRA) ++ { ++ int offset; ++ int number = op - OP_BRA; ++ ++ /* For extended extraction brackets (large number), we have to fish out the ++ number from a dummy opcode at the start. */ ++ ++ if (number > EXTRACT_BASIC_MAX) number = (ecode[4] << 8) | ecode[5]; ++ offset = number << 1; ++ ++#ifdef DEBUG ++ PCRE_PRINTF("start bracket %d subject=", number); ++ pchars(eptr, 16, TRUE, md); ++ PCRE_PRINTF("\n"); ++#endif ++ ++ if (offset < md->offset_max) ++ { ++ int save_offset1 = md->offset_vector[offset]; ++ int save_offset2 = md->offset_vector[offset+1]; ++ int save_offset3 = md->offset_vector[md->offset_end - number]; ++ ++ DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); ++ md->offset_vector[md->offset_end - number] = eptr - md->start_subject; ++ ++ do ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup)) ++ return TRUE; ++ ecode += (ecode[1] << 8) + ecode[2]; ++ } ++ while (*ecode == OP_ALT); ++ ++ DPRINTF(("bracket %d failed\n", number)); ++ ++ md->offset_vector[offset] = save_offset1; ++ md->offset_vector[offset+1] = save_offset2; ++ md->offset_vector[md->offset_end - number] = save_offset3; ++ ++ return FALSE; ++ } ++ ++ /* Insufficient room for saving captured contents */ ++ ++ else op = OP_BRA; ++ } ++ ++ /* Other types of node can be handled by a switch */ ++ ++ switch(op) ++ { ++ case OP_BRA: /* Non-capturing bracket: optimized */ ++ DPRINTF(("start bracket 0\n")); ++ do ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup)) ++ return TRUE; ++ ecode += (ecode[1] << 8) + ecode[2]; ++ } ++ while (*ecode == OP_ALT); ++ DPRINTF(("bracket 0 failed\n")); ++ return FALSE; ++ ++ /* Conditional group: compilation checked that there are no more than ++ two branches. If the condition is false, skipping the first branch takes us ++ past the end if there is only one branch, but that's OK because that is ++ exactly what going to the ket would do. */ ++ ++ case OP_COND: ++ if (ecode[3] == OP_CREF) /* Condition is extraction test */ ++ { ++ int offset = (ecode[4] << 9) | (ecode[5] << 1); /* Doubled ref number */ ++ return match(eptr, ++ ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)? ++ 6 : 3 + (ecode[1] << 8) + ecode[2]), ++ offset_top, md, ims, eptrb, match_isgroup); ++ } ++ ++ /* The condition is an assertion. Call match() to evaluate it - setting ++ the final argument TRUE causes it to stop at the end of an assertion. */ ++ ++ else ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, NULL, ++ match_condassert | match_isgroup)) ++ { ++ ecode += 3 + (ecode[4] << 8) + ecode[5]; ++ while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2]; ++ } ++ else ecode += (ecode[1] << 8) + ecode[2]; ++ return match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup); ++ } ++ /* Control never reaches here */ ++ ++ /* Skip over conditional reference or large extraction number data if ++ encountered. */ ++ ++ case OP_CREF: ++ case OP_BRANUMBER: ++ ecode += 3; ++ break; ++ ++ /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched ++ an empty string - recursion will then try other alternatives, if any. */ ++ ++ case OP_END: ++ if (md->notempty && eptr == md->start_match) return FALSE; ++ md->end_match_ptr = eptr; /* Record where we ended */ ++ md->end_offset_top = offset_top; /* and how many extracts were taken */ ++ return TRUE; ++ ++ /* Change option settings */ ++ ++ case OP_OPT: ++ ims = ecode[1]; ++ ecode += 2; ++ DPRINTF(("ims set to %02lx\n", ims)); ++ break; ++ ++ /* Assertion brackets. Check the alternative branches in turn - the ++ matching won't pass the KET for an assertion. If any one branch matches, ++ the assertion is true. Lookbehind assertions have an OP_REVERSE item at the ++ start of each branch to move the current point backwards, so the code at ++ this level is identical to the lookahead case. */ ++ ++ case OP_ASSERT: ++ case OP_ASSERTBACK: ++ do ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) break; ++ ecode += (ecode[1] << 8) + ecode[2]; ++ } ++ while (*ecode == OP_ALT); ++ if (*ecode == OP_KET) return FALSE; ++ ++ /* If checking an assertion for a condition, return TRUE. */ ++ ++ if ((flags & match_condassert) != 0) return TRUE; ++ ++ /* Continue from after the assertion, updating the offsets high water ++ mark, since extracts may have been taken during the assertion. */ ++ ++ do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); ++ ecode += 3; ++ offset_top = md->end_offset_top; ++ continue; ++ ++ /* Negative assertion: all branches must fail to match */ ++ ++ case OP_ASSERT_NOT: ++ case OP_ASSERTBACK_NOT: ++ do ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) ++ return FALSE; ++ ecode += (ecode[1] << 8) + ecode[2]; ++ } ++ while (*ecode == OP_ALT); ++ ++ if ((flags & match_condassert) != 0) return TRUE; ++ ++ ecode += 3; ++ continue; ++ ++ /* Move the subject pointer back. This occurs only at the start of ++ each branch of a lookbehind assertion. If we are too close to the start to ++ move back, this match function fails. When working with UTF-8 we move ++ back a number of characters, not bytes. */ ++ ++ case OP_REVERSE: ++#ifdef SUPPORT_UTF8 ++ c = (ecode[1] << 8) + ecode[2]; ++ for (i = 0; i < c; i++) ++ { ++ eptr--; ++ BACKCHAR(eptr) ++ } ++#else ++ eptr -= (ecode[1] << 8) + ecode[2]; ++#endif ++ ++ if (eptr < md->start_subject) return FALSE; ++ ecode += 3; ++ break; ++ ++ /* Recursion matches the current regex, nested. If there are any capturing ++ brackets started but not finished, we have to save their starting points ++ and reinstate them after the recursion. However, we don't know how many ++ such there are (offset_top records the completed total) so we just have ++ to save all the potential data. There may be up to 99 such values, which ++ is a bit large to put on the stack, but using malloc for small numbers ++ seems expensive. As a compromise, the stack is used when there are fewer ++ than 16 values to store; otherwise malloc is used. A problem is what to do ++ if the malloc fails ... there is no way of returning to the top level with ++ an error. Save the top 15 values on the stack, and accept that the rest ++ may be wrong. */ ++ ++ case OP_RECURSE: ++ { ++ BOOL rc; ++ int *save; ++ int stacksave[15]; ++ ++ c = md->offset_max; ++ ++ if (c < 16) save = stacksave; else ++ { ++ save = (int *)(pcre_malloc)((c+1) * sizeof(int)); ++ if (save == NULL) ++ { ++ save = stacksave; ++ c = 15; ++ } ++ } ++ ++ for (i = 1; i <= c; i++) ++ save[i] = md->offset_vector[md->offset_end - i]; ++ rc = match(eptr, md->start_pattern, offset_top, md, ims, eptrb, ++ match_isgroup); ++ for (i = 1; i <= c; i++) ++ md->offset_vector[md->offset_end - i] = save[i]; ++ if (save != stacksave) (pcre_free)(save); ++ if (!rc) return FALSE; ++ ++ /* In case the recursion has set more capturing values, save the final ++ number, then move along the subject till after the recursive match, ++ and advance one byte in the pattern code. */ ++ ++ offset_top = md->end_offset_top; ++ eptr = md->end_match_ptr; ++ ecode++; ++ } ++ break; ++ ++ /* "Once" brackets are like assertion brackets except that after a match, ++ the point in the subject string is not moved back. Thus there can never be ++ a move back into the brackets. Check the alternative branches in turn - the ++ matching won't pass the KET for this kind of subpattern. If any one branch ++ matches, we carry on as at the end of a normal bracket, leaving the subject ++ pointer. */ ++ ++ case OP_ONCE: ++ { ++ const uschar *prev = ecode; ++ const uschar *saved_eptr = eptr; ++ ++ do ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup)) ++ break; ++ ecode += (ecode[1] << 8) + ecode[2]; ++ } ++ while (*ecode == OP_ALT); ++ ++ /* If hit the end of the group (which could be repeated), fail */ ++ ++ if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE; ++ ++ /* Continue as from after the assertion, updating the offsets high water ++ mark, since extracts may have been taken. */ ++ ++ do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); ++ ++ offset_top = md->end_offset_top; ++ eptr = md->end_match_ptr; ++ ++ /* For a non-repeating ket, just continue at this level. This also ++ happens for a repeating ket if no characters were matched in the group. ++ This is the forcible breaking of infinite loops as implemented in Perl ++ 5.005. If there is an options reset, it will get obeyed in the normal ++ course of events. */ ++ ++ if (*ecode == OP_KET || eptr == saved_eptr) ++ { ++ ecode += 3; ++ break; ++ } ++ ++ /* The repeating kets try the rest of the pattern or restart from the ++ preceding bracket, in the appropriate order. We need to reset any options ++ that changed within the bracket before re-running it, so check the next ++ opcode. */ ++ ++ if (ecode[3] == OP_OPT) ++ { ++ ims = (ims & ~PCRE_IMS) | ecode[4]; ++ DPRINTF(("ims set to %02lx at group repeat\n", ims)); ++ } ++ ++ if (*ecode == OP_KETRMIN) ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) || ++ match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup)) ++ return TRUE; ++ } ++ else /* OP_KETRMAX */ ++ { ++ if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) || ++ match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE; ++ } ++ } ++ return FALSE; ++ ++ /* An alternation is the end of a branch; scan along to find the end of the ++ bracketed group and go to there. */ ++ ++ case OP_ALT: ++ do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); ++ break; ++ ++ /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating ++ that it may occur zero times. It may repeat infinitely, or not at all - ++ i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper ++ repeat limits are compiled as a number of copies, with the optional ones ++ preceded by BRAZERO or BRAMINZERO. */ ++ ++ case OP_BRAZERO: ++ { ++ const uschar *next = ecode+1; ++ if (match(eptr, next, offset_top, md, ims, eptrb, match_isgroup)) ++ return TRUE; ++ do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); ++ ecode = next + 3; ++ } ++ break; ++ ++ case OP_BRAMINZERO: ++ { ++ const uschar *next = ecode+1; ++ do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); ++ if (match(eptr, next+3, offset_top, md, ims, eptrb, match_isgroup)) ++ return TRUE; ++ ecode++; ++ } ++ break; ++ ++ /* End of a group, repeated or non-repeating. If we are at the end of ++ an assertion "group", stop matching and return TRUE, but record the ++ current high water mark for use by positive assertions. Do this also ++ for the "once" (not-backup up) groups. */ ++ ++ case OP_KET: ++ case OP_KETRMIN: ++ case OP_KETRMAX: ++ { ++ const uschar *prev = ecode - (ecode[1] << 8) - ecode[2]; ++ const uschar *saved_eptr = eptrb->saved_eptr; ++ ++ eptrb = eptrb->prev; /* Back up the stack of bracket start pointers */ ++ ++ if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || ++ *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || ++ *prev == OP_ONCE) ++ { ++ md->end_match_ptr = eptr; /* For ONCE */ ++ md->end_offset_top = offset_top; ++ return TRUE; ++ } ++ ++ /* In all other cases except a conditional group we have to check the ++ group number back at the start and if necessary complete handling an ++ extraction by setting the offsets and bumping the high water mark. */ ++ ++ if (*prev != OP_COND) ++ { ++ int offset; ++ int number = *prev - OP_BRA; ++ ++ /* For extended extraction brackets (large number), we have to fish out ++ the number from a dummy opcode at the start. */ ++ ++ if (number > EXTRACT_BASIC_MAX) number = (prev[4] << 8) | prev[5]; ++ offset = number << 1; ++ ++#ifdef DEBUG ++ PCRE_PRINTF("end bracket %d", number); ++ PCRE_PRINTF("\n"); ++#endif ++ ++ if (number > 0) ++ { ++ if (offset >= md->offset_max) md->offset_overflow = TRUE; else ++ { ++ md->offset_vector[offset] = ++ md->offset_vector[md->offset_end - number]; ++ md->offset_vector[offset+1] = eptr - md->start_subject; ++ if (offset_top <= offset) offset_top = offset + 2; ++ } ++ } ++ } ++ ++ /* Reset the value of the ims flags, in case they got changed during ++ the group. */ ++ ++ ims = original_ims; ++ DPRINTF(("ims reset to %02lx\n", ims)); ++ ++ /* For a non-repeating ket, just continue at this level. This also ++ happens for a repeating ket if no characters were matched in the group. ++ This is the forcible breaking of infinite loops as implemented in Perl ++ 5.005. If there is an options reset, it will get obeyed in the normal ++ course of events. */ ++ ++ if (*ecode == OP_KET || eptr == saved_eptr) ++ { ++ ecode += 3; ++ break; ++ } ++ ++ /* The repeating kets try the rest of the pattern or restart from the ++ preceding bracket, in the appropriate order. */ ++ ++ if (*ecode == OP_KETRMIN) ++ { ++ if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) || ++ match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup)) ++ return TRUE; ++ } ++ else /* OP_KETRMAX */ ++ { ++ if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) || ++ match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE; ++ } ++ } ++ return FALSE; ++ ++ /* Start of subject unless notbol, or after internal newline if multiline */ ++ ++ case OP_CIRC: ++ if (md->notbol && eptr == md->start_subject) return FALSE; ++ if ((ims & PCRE_MULTILINE) != 0) ++ { ++ if (eptr != md->start_subject && eptr[-1] != NEWLINE) return FALSE; ++ ecode++; ++ break; ++ } ++ /* ... else fall through */ ++ ++ /* Start of subject assertion */ ++ ++ case OP_SOD: ++ if (eptr != md->start_subject) return FALSE; ++ ecode++; ++ break; ++ ++ /* Assert before internal newline if multiline, or before a terminating ++ newline unless endonly is set, else end of subject unless noteol is set. */ ++ ++ case OP_DOLL: ++ if ((ims & PCRE_MULTILINE) != 0) ++ { ++ if (eptr < md->end_subject) { if (*eptr != NEWLINE) return FALSE; } ++ else { if (md->noteol) return FALSE; } ++ ecode++; ++ break; ++ } ++ else ++ { ++ if (md->noteol) return FALSE; ++ if (!md->endonly) ++ { ++ if (eptr < md->end_subject - 1 || ++ (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE; ++ ++ ecode++; ++ break; ++ } ++ } ++ /* ... else fall through */ ++ ++ /* End of subject assertion (\z) */ ++ ++ case OP_EOD: ++ if (eptr < md->end_subject) return FALSE; ++ ecode++; ++ break; ++ ++ /* End of subject or ending \n assertion (\Z) */ ++ ++ case OP_EODN: ++ if (eptr < md->end_subject - 1 || ++ (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE; ++ ecode++; ++ break; ++ ++ /* Word boundary assertions */ ++ ++ case OP_NOT_WORD_BOUNDARY: ++ case OP_WORD_BOUNDARY: ++ { ++ BOOL prev_is_word = (eptr != md->start_subject) && ++ ((md->ctypes[eptr[-1]] & ctype_word) != 0); ++ BOOL cur_is_word = (eptr < md->end_subject) && ++ ((md->ctypes[*eptr] & ctype_word) != 0); ++ if ((*ecode++ == OP_WORD_BOUNDARY)? ++ cur_is_word == prev_is_word : cur_is_word != prev_is_word) ++ return FALSE; ++ } ++ break; ++ ++ /* Match a single character type; inline for speed */ ++ ++ case OP_ANY: ++ if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE) ++ return FALSE; ++ if (eptr++ >= md->end_subject) return FALSE; ++#ifdef SUPPORT_UTF8 ++ if (md->utf8) ++ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; ++#endif ++ ecode++; ++ break; ++ ++ case OP_NOT_DIGIT: ++ if (eptr >= md->end_subject || ++ (md->ctypes[*eptr++] & ctype_digit) != 0) ++ return FALSE; ++ ecode++; ++ break; ++ ++ case OP_DIGIT: ++ if (eptr >= md->end_subject || ++ (md->ctypes[*eptr++] & ctype_digit) == 0) ++ return FALSE; ++ ecode++; ++ break; ++ ++ case OP_NOT_WHITESPACE: ++ if (eptr >= md->end_subject || ++ (md->ctypes[*eptr++] & ctype_space) != 0) ++ return FALSE; ++ ecode++; ++ break; ++ ++ case OP_WHITESPACE: ++ if (eptr >= md->end_subject || ++ (md->ctypes[*eptr++] & ctype_space) == 0) ++ return FALSE; ++ ecode++; ++ break; ++ ++ case OP_NOT_WORDCHAR: ++ if (eptr >= md->end_subject || ++ (md->ctypes[*eptr++] & ctype_word) != 0) ++ return FALSE; ++ ecode++; ++ break; ++ ++ case OP_WORDCHAR: ++ if (eptr >= md->end_subject || ++ (md->ctypes[*eptr++] & ctype_word) == 0) ++ return FALSE; ++ ecode++; ++ break; ++ ++ /* Match a back reference, possibly repeatedly. Look past the end of the ++ item to see if there is repeat information following. The code is similar ++ to that for character classes, but repeated for efficiency. Then obey ++ similar code to character type repeats - written out again for speed. ++ However, if the referenced string is the empty string, always treat ++ it as matched, any number of times (otherwise there could be infinite ++ loops). */ ++ ++ case OP_REF: ++ { ++ int length; ++ int offset = (ecode[1] << 9) | (ecode[2] << 1); /* Doubled ref number */ ++ ecode += 3; /* Advance past item */ ++ ++ /* If the reference is unset, set the length to be longer than the amount ++ of subject left; this ensures that every attempt at a match fails. We ++ can't just fail here, because of the possibility of quantifiers with zero ++ minima. */ ++ ++ length = (offset >= offset_top || md->offset_vector[offset] < 0)? ++ md->end_subject - eptr + 1 : ++ md->offset_vector[offset+1] - md->offset_vector[offset]; ++ ++ /* Set up for repetition, or handle the non-repeated case */ ++ ++ switch (*ecode) ++ { ++ case OP_CRSTAR: ++ case OP_CRMINSTAR: ++ case OP_CRPLUS: ++ case OP_CRMINPLUS: ++ case OP_CRQUERY: ++ case OP_CRMINQUERY: ++ c = *ecode++ - OP_CRSTAR; ++ minimize = (c & 1) != 0; ++ min = rep_min[c]; /* Pick up values from tables; */ ++ max = rep_max[c]; /* zero for max => infinity */ ++ if (max == 0) max = INT_MAX; ++ break; ++ ++ case OP_CRRANGE: ++ case OP_CRMINRANGE: ++ minimize = (*ecode == OP_CRMINRANGE); ++ min = (ecode[1] << 8) + ecode[2]; ++ max = (ecode[3] << 8) + ecode[4]; ++ if (max == 0) max = INT_MAX; ++ ecode += 5; ++ break; ++ ++ default: /* No repeat follows */ ++ if (!match_ref(offset, eptr, length, md, ims)) return FALSE; ++ eptr += length; ++ continue; /* With the main loop */ ++ } ++ ++ /* If the length of the reference is zero, just continue with the ++ main loop. */ ++ ++ if (length == 0) continue; ++ ++ /* First, ensure the minimum number of matches are present. We get back ++ the length of the reference string explicitly rather than passing the ++ address of eptr, so that eptr can be a register variable. */ ++ ++ for (i = 1; i <= min; i++) ++ { ++ if (!match_ref(offset, eptr, length, md, ims)) return FALSE; ++ eptr += length; ++ } ++ ++ /* If min = max, continue at the same level without recursion. ++ They are not both allowed to be zero. */ ++ ++ if (min == max) continue; ++ ++ /* If minimizing, keep trying and advancing the pointer */ ++ ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ if (i >= max || !match_ref(offset, eptr, length, md, ims)) ++ return FALSE; ++ eptr += length; ++ } ++ /* Control never gets here */ ++ } ++ ++ /* If maximizing, find the longest string and work backwards */ ++ ++ else ++ { ++ const uschar *pp = eptr; ++ for (i = min; i < max; i++) ++ { ++ if (!match_ref(offset, eptr, length, md, ims)) break; ++ eptr += length; ++ } ++ while (eptr >= pp) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ eptr -= length; ++ } ++ return FALSE; ++ } ++ } ++ /* Control never gets here */ ++ ++ ++ ++ /* Match a character class, possibly repeatedly. Look past the end of the ++ item to see if there is repeat information following. Then obey similar ++ code to character type repeats - written out again for speed. */ ++ ++ case OP_CLASS: ++ { ++ const uschar *data = ecode + 1; /* Save for matching */ ++ ecode += 33; /* Advance past the item */ ++ ++ switch (*ecode) ++ { ++ case OP_CRSTAR: ++ case OP_CRMINSTAR: ++ case OP_CRPLUS: ++ case OP_CRMINPLUS: ++ case OP_CRQUERY: ++ case OP_CRMINQUERY: ++ c = *ecode++ - OP_CRSTAR; ++ minimize = (c & 1) != 0; ++ min = rep_min[c]; /* Pick up values from tables; */ ++ max = rep_max[c]; /* zero for max => infinity */ ++ if (max == 0) max = INT_MAX; ++ break; ++ ++ case OP_CRRANGE: ++ case OP_CRMINRANGE: ++ minimize = (*ecode == OP_CRMINRANGE); ++ min = (ecode[1] << 8) + ecode[2]; ++ max = (ecode[3] << 8) + ecode[4]; ++ if (max == 0) max = INT_MAX; ++ ecode += 5; ++ break; ++ ++ default: /* No repeat follows */ ++ min = max = 1; ++ break; ++ } ++ ++ /* First, ensure the minimum number of matches are present. */ ++ ++ for (i = 1; i <= min; i++) ++ { ++ if (eptr >= md->end_subject) return FALSE; ++ GETCHARINC(c, eptr) /* Get character; increment eptr */ ++ ++#ifdef SUPPORT_UTF8 ++ /* We do not yet support class members > 255 */ ++ if (c > 255) return FALSE; ++#endif ++ ++ if ((data[c/8] & (1 << (c&7))) != 0) continue; ++ return FALSE; ++ } ++ ++ /* If max == min we can continue with the main loop without the ++ need to recurse. */ ++ ++ if (min == max) continue; ++ ++ /* If minimizing, keep testing the rest of the expression and advancing ++ the pointer while it matches the class. */ ++ ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ if (i >= max || eptr >= md->end_subject) return FALSE; ++ GETCHARINC(c, eptr) /* Get character; increment eptr */ ++ ++#ifdef SUPPORT_UTF8 ++ /* We do not yet support class members > 255 */ ++ if (c > 255) return FALSE; ++#endif ++ if ((data[c/8] & (1 << (c&7))) != 0) continue; ++ return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ ++ /* If maximizing, find the longest possible run, then work backwards. */ ++ ++ else ++ { ++ const uschar *pp = eptr; ++ int len = 1; ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject) break; ++ GETCHARLEN(c, eptr, len) /* Get character, set length if UTF-8 */ ++ ++#ifdef SUPPORT_UTF8 ++ /* We do not yet support class members > 255 */ ++ if (c > 255) break; ++#endif ++ if ((data[c/8] & (1 << (c&7))) == 0) break; ++ eptr += len; ++ } ++ ++ while (eptr >= pp) ++ { ++ if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ ++#ifdef SUPPORT_UTF8 ++ BACKCHAR(eptr) ++#endif ++ } ++ return FALSE; ++ } ++ } ++ /* Control never gets here */ ++ ++ /* Match a run of characters */ ++ ++ case OP_CHARS: ++ { ++ register int length = ecode[1]; ++ ecode += 2; ++ ++#ifdef DEBUG /* Sigh. Some compilers never learn. */ ++ if (eptr >= md->end_subject) ++ PCRE_PRINTF("matching subject against pattern "); ++ else ++ { ++ PCRE_PRINTF("matching subject "); ++ pchars(eptr, length, TRUE, md); ++ PCRE_PRINTF(" against pattern "); ++ } ++ pchars(ecode, length, FALSE, md); ++ PCRE_PRINTF("\n"); ++#endif ++ ++ if (length > md->end_subject - eptr) return FALSE; ++ if ((ims & PCRE_CASELESS) != 0) ++ { ++ while (length-- > 0) ++ if (md->lcc[*ecode++] != md->lcc[*eptr++]) ++ return FALSE; ++ } ++ else ++ { ++ while (length-- > 0) if (*ecode++ != *eptr++) return FALSE; ++ } ++ } ++ break; ++ ++ /* Match a single character repeatedly; different opcodes share code. */ ++ ++ case OP_EXACT: ++ min = max = (ecode[1] << 8) + ecode[2]; ++ ecode += 3; ++ goto REPEATCHAR; ++ ++ case OP_UPTO: ++ case OP_MINUPTO: ++ min = 0; ++ max = (ecode[1] << 8) + ecode[2]; ++ minimize = *ecode == OP_MINUPTO; ++ ecode += 3; ++ goto REPEATCHAR; ++ ++ case OP_STAR: ++ case OP_MINSTAR: ++ case OP_PLUS: ++ case OP_MINPLUS: ++ case OP_QUERY: ++ case OP_MINQUERY: ++ c = *ecode++ - OP_STAR; ++ minimize = (c & 1) != 0; ++ min = rep_min[c]; /* Pick up values from tables; */ ++ max = rep_max[c]; /* zero for max => infinity */ ++ if (max == 0) max = INT_MAX; ++ ++ /* Common code for all repeated single-character matches. We can give ++ up quickly if there are fewer than the minimum number of characters left in ++ the subject. */ ++ ++ REPEATCHAR: ++ if (min > md->end_subject - eptr) return FALSE; ++ c = *ecode++; ++ ++ /* The code is duplicated for the caseless and caseful cases, for speed, ++ since matching characters is likely to be quite common. First, ensure the ++ minimum number of matches are present. If min = max, continue at the same ++ level without recursing. Otherwise, if minimizing, keep trying the rest of ++ the expression and advancing one matching character if failing, up to the ++ maximum. Alternatively, if maximizing, find the maximum number of ++ characters and work backwards. */ ++ ++ DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, ++ max, eptr)); ++ ++ if ((ims & PCRE_CASELESS) != 0) ++ { ++ c = md->lcc[c]; ++ for (i = 1; i <= min; i++) ++ if (c != md->lcc[*eptr++]) return FALSE; ++ if (min == max) continue; ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ if (i >= max || eptr >= md->end_subject || ++ c != md->lcc[*eptr++]) ++ return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ else ++ { ++ const uschar *pp = eptr; ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || c != md->lcc[*eptr]) break; ++ eptr++; ++ } ++ while (eptr >= pp) ++ if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ ++ /* Caseful comparisons */ ++ ++ else ++ { ++ for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE; ++ if (min == max) continue; ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ else ++ { ++ const uschar *pp = eptr; ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || c != *eptr) break; ++ eptr++; ++ } ++ while (eptr >= pp) ++ if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ return FALSE; ++ } ++ } ++ /* Control never gets here */ ++ ++ /* Match a negated single character */ ++ ++ case OP_NOT: ++ if (eptr >= md->end_subject) return FALSE; ++ ecode++; ++ if ((ims & PCRE_CASELESS) != 0) ++ { ++ if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE; ++ } ++ else ++ { ++ if (*ecode++ == *eptr++) return FALSE; ++ } ++ break; ++ ++ /* Match a negated single character repeatedly. This is almost a repeat of ++ the code for a repeated single character, but I haven't found a nice way of ++ commoning these up that doesn't require a test of the positive/negative ++ option for each character match. Maybe that wouldn't add very much to the ++ time taken, but character matching *is* what this is all about... */ ++ ++ case OP_NOTEXACT: ++ min = max = (ecode[1] << 8) + ecode[2]; ++ ecode += 3; ++ goto REPEATNOTCHAR; ++ ++ case OP_NOTUPTO: ++ case OP_NOTMINUPTO: ++ min = 0; ++ max = (ecode[1] << 8) + ecode[2]; ++ minimize = *ecode == OP_NOTMINUPTO; ++ ecode += 3; ++ goto REPEATNOTCHAR; ++ ++ case OP_NOTSTAR: ++ case OP_NOTMINSTAR: ++ case OP_NOTPLUS: ++ case OP_NOTMINPLUS: ++ case OP_NOTQUERY: ++ case OP_NOTMINQUERY: ++ c = *ecode++ - OP_NOTSTAR; ++ minimize = (c & 1) != 0; ++ min = rep_min[c]; /* Pick up values from tables; */ ++ max = rep_max[c]; /* zero for max => infinity */ ++ if (max == 0) max = INT_MAX; ++ ++ /* Common code for all repeated single-character matches. We can give ++ up quickly if there are fewer than the minimum number of characters left in ++ the subject. */ ++ ++ REPEATNOTCHAR: ++ if (min > md->end_subject - eptr) return FALSE; ++ c = *ecode++; ++ ++ /* The code is duplicated for the caseless and caseful cases, for speed, ++ since matching characters is likely to be quite common. First, ensure the ++ minimum number of matches are present. If min = max, continue at the same ++ level without recursing. Otherwise, if minimizing, keep trying the rest of ++ the expression and advancing one matching character if failing, up to the ++ maximum. Alternatively, if maximizing, find the maximum number of ++ characters and work backwards. */ ++ ++ DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max, ++ max, eptr)); ++ ++ if ((ims & PCRE_CASELESS) != 0) ++ { ++ c = md->lcc[c]; ++ for (i = 1; i <= min; i++) ++ if (c == md->lcc[*eptr++]) return FALSE; ++ if (min == max) continue; ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ if (i >= max || eptr >= md->end_subject || ++ c == md->lcc[*eptr++]) ++ return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ else ++ { ++ const uschar *pp = eptr; ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || c == md->lcc[*eptr]) break; ++ eptr++; ++ } ++ while (eptr >= pp) ++ if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ ++ /* Caseful comparisons */ ++ ++ else ++ { ++ for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE; ++ if (min == max) continue; ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE; ++ } ++ /* Control never gets here */ ++ } ++ else ++ { ++ const uschar *pp = eptr; ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || c == *eptr) break; ++ eptr++; ++ } ++ while (eptr >= pp) ++ if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++ return FALSE; ++ } ++ } ++ /* Control never gets here */ ++ ++ /* Match a single character type repeatedly; several different opcodes ++ share code. This is very similar to the code for single characters, but we ++ repeat it in the interests of efficiency. */ ++ ++ case OP_TYPEEXACT: ++ min = max = (ecode[1] << 8) + ecode[2]; ++ minimize = TRUE; ++ ecode += 3; ++ goto REPEATTYPE; ++ ++ case OP_TYPEUPTO: ++ case OP_TYPEMINUPTO: ++ min = 0; ++ max = (ecode[1] << 8) + ecode[2]; ++ minimize = *ecode == OP_TYPEMINUPTO; ++ ecode += 3; ++ goto REPEATTYPE; ++ ++ case OP_TYPESTAR: ++ case OP_TYPEMINSTAR: ++ case OP_TYPEPLUS: ++ case OP_TYPEMINPLUS: ++ case OP_TYPEQUERY: ++ case OP_TYPEMINQUERY: ++ c = *ecode++ - OP_TYPESTAR; ++ minimize = (c & 1) != 0; ++ min = rep_min[c]; /* Pick up values from tables; */ ++ max = rep_max[c]; /* zero for max => infinity */ ++ if (max == 0) max = INT_MAX; ++ ++ /* Common code for all repeated single character type matches */ ++ ++ REPEATTYPE: ++ ctype = *ecode++; /* Code for the character type */ ++ ++ /* First, ensure the minimum number of matches are present. Use inline ++ code for maximizing the speed, and do the type test once at the start ++ (i.e. keep it out of the loop). Also we can test that there are at least ++ the minimum number of bytes before we start, except when doing '.' in ++ UTF8 mode. Leave the test in in all cases; in the special case we have ++ to test after each character. */ ++ ++ if (min > md->end_subject - eptr) return FALSE; ++ if (min > 0) switch(ctype) ++ { ++ case OP_ANY: ++#ifdef SUPPORT_UTF8 ++ if (md->utf8) ++ { ++ for (i = 1; i <= min; i++) ++ { ++ if (eptr >= md->end_subject || ++ (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0)) ++ return FALSE; ++ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; ++ } ++ break; ++ } ++#endif ++ /* Non-UTF8 can be faster */ ++ if ((ims & PCRE_DOTALL) == 0) ++ { for (i = 1; i <= min; i++) if (*eptr++ == NEWLINE) return FALSE; } ++ else eptr += min; ++ break; ++ ++ case OP_NOT_DIGIT: ++ for (i = 1; i <= min; i++) ++ if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE; ++ break; ++ ++ case OP_DIGIT: ++ for (i = 1; i <= min; i++) ++ if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE; ++ break; ++ ++ case OP_NOT_WHITESPACE: ++ for (i = 1; i <= min; i++) ++ if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE; ++ break; ++ ++ case OP_WHITESPACE: ++ for (i = 1; i <= min; i++) ++ if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE; ++ break; ++ ++ case OP_NOT_WORDCHAR: ++ for (i = 1; i <= min; i++) ++ if ((md->ctypes[*eptr++] & ctype_word) != 0) ++ return FALSE; ++ break; ++ ++ case OP_WORDCHAR: ++ for (i = 1; i <= min; i++) ++ if ((md->ctypes[*eptr++] & ctype_word) == 0) ++ return FALSE; ++ break; ++ } ++ ++ /* If min = max, continue at the same level without recursing */ ++ ++ if (min == max) continue; ++ ++ /* If minimizing, we have to test the rest of the pattern before each ++ subsequent match. */ ++ ++ if (minimize) ++ { ++ for (i = min;; i++) ++ { ++ if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) return TRUE; ++ if (i >= max || eptr >= md->end_subject) return FALSE; ++ ++ c = *eptr++; ++ switch(ctype) ++ { ++ case OP_ANY: ++ if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return FALSE; ++#ifdef SUPPORT_UTF8 ++ if (md->utf8) ++ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; ++#endif ++ break; ++ ++ case OP_NOT_DIGIT: ++ if ((md->ctypes[c] & ctype_digit) != 0) return FALSE; ++ break; ++ ++ case OP_DIGIT: ++ if ((md->ctypes[c] & ctype_digit) == 0) return FALSE; ++ break; ++ ++ case OP_NOT_WHITESPACE: ++ if ((md->ctypes[c] & ctype_space) != 0) return FALSE; ++ break; ++ ++ case OP_WHITESPACE: ++ if ((md->ctypes[c] & ctype_space) == 0) return FALSE; ++ break; ++ ++ case OP_NOT_WORDCHAR: ++ if ((md->ctypes[c] & ctype_word) != 0) return FALSE; ++ break; ++ ++ case OP_WORDCHAR: ++ if ((md->ctypes[c] & ctype_word) == 0) return FALSE; ++ break; ++ } ++ } ++ /* Control never gets here */ ++ } ++ ++ /* If maximizing it is worth using inline code for speed, doing the type ++ test once at the start (i.e. keep it out of the loop). */ ++ ++ else ++ { ++ const uschar *pp = eptr; ++ switch(ctype) ++ { ++ case OP_ANY: ++ ++ /* Special code is required for UTF8, but when the maximum is unlimited ++ we don't need it. */ ++ ++#ifdef SUPPORT_UTF8 ++ if (md->utf8 && max < INT_MAX) ++ { ++ if ((ims & PCRE_DOTALL) == 0) ++ { ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || *eptr++ == NEWLINE) break; ++ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; ++ } ++ } ++ else ++ { ++ for (i = min; i < max; i++) ++ { ++ eptr++; ++ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; ++ } ++ } ++ break; ++ } ++#endif ++ /* Non-UTF8 can be faster */ ++ if ((ims & PCRE_DOTALL) == 0) ++ { ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || *eptr == NEWLINE) break; ++ eptr++; ++ } ++ } ++ else ++ { ++ c = max - min; ++ if (c > md->end_subject - eptr) c = md->end_subject - eptr; ++ eptr += c; ++ } ++ break; ++ ++ case OP_NOT_DIGIT: ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) ++ break; ++ eptr++; ++ } ++ break; ++ ++ case OP_DIGIT: ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) ++ break; ++ eptr++; ++ } ++ break; ++ ++ case OP_NOT_WHITESPACE: ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) ++ break; ++ eptr++; ++ } ++ break; ++ ++ case OP_WHITESPACE: ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) ++ break; ++ eptr++; ++ } ++ break; ++ ++ case OP_NOT_WORDCHAR: ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) ++ break; ++ eptr++; ++ } ++ break; ++ ++ case OP_WORDCHAR: ++ for (i = min; i < max; i++) ++ { ++ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) ++ break; ++ eptr++; ++ } ++ break; ++ } ++ ++ while (eptr >= pp) ++ { ++ if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) ++ return TRUE; ++#ifdef SUPPORT_UTF8 ++ if (md->utf8) ++ while (eptr > pp && (*eptr & 0xc0) == 0x80) eptr--; ++#endif ++ } ++ return FALSE; ++ } ++ /* Control never gets here */ ++ ++ /* There's been some horrible disaster. */ ++ ++ default: ++ DPRINTF(("Unknown opcode %d\n", *ecode)); ++ md->errorcode = PCRE_ERROR_UNKNOWN_NODE; ++ return FALSE; ++ } ++ ++ /* Do not stick any code in here without much thought; it is assumed ++ that "continue" in the code above comes out to here to repeat the main ++ loop. */ ++ ++ } /* End of main loop */ ++/* Control never reaches here */ ++} ++ ++ ++/************************************************* ++* Execute a Regular Expression * ++*************************************************/ ++ ++/* This function applies a compiled re to a subject string and picks out ++portions of the string if it matches. Two elements in the vector are set for ++each substring: the offsets to the start and end of the substring. ++ ++Arguments: ++ external_re points to the compiled expression ++ external_extra points to "hints" from pcre_study() or is NULL ++ subject points to the subject string ++ length length of subject string (may contain binary zeros) ++ start_offset where to start in the subject string ++ options option bits ++ offsets points to a vector of ints to be filled in with offsets ++ offsetcount the number of elements in the vector ++ ++Returns: > 0 => success; value is the number of elements filled in ++ = 0 => success, but offsets is not big enough ++ -1 => failed to match ++ < -1 => some kind of unexpected problem ++*/ ++ ++int ++pcre_exec(const pcre *external_re, const pcre_extra *external_extra, ++ const char *subject, int length, int start_offset, int options, int *offsets, ++ int offsetcount) ++{ ++int resetcount, ocount; ++int first_char = -1; ++int req_char = -1; ++int req_char2 = -1; ++unsigned long int ims = 0; ++match_data match_block; ++const uschar *start_bits = NULL; ++const uschar *start_match = (const uschar *)subject + start_offset; ++const uschar *end_subject; ++const uschar *req_char_ptr = start_match - 1; ++const real_pcre *re = (const real_pcre *)external_re; ++const real_pcre_extra *extra = (const real_pcre_extra *)external_extra; ++BOOL using_temporary_offsets = FALSE; ++BOOL anchored; ++BOOL startline; ++ ++if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; ++ ++if (re == NULL || subject == NULL || ++ (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; ++if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; ++ ++anchored = ((re->options | options) & PCRE_ANCHORED) != 0; ++startline = (re->options & PCRE_STARTLINE) != 0; ++ ++match_block.start_pattern = re->code; ++match_block.start_subject = (const uschar *)subject; ++match_block.end_subject = match_block.start_subject + length; ++end_subject = match_block.end_subject; ++ ++match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; ++match_block.utf8 = (re->options & PCRE_UTF8) != 0; ++ ++match_block.notbol = (options & PCRE_NOTBOL) != 0; ++match_block.noteol = (options & PCRE_NOTEOL) != 0; ++match_block.notempty = (options & PCRE_NOTEMPTY) != 0; ++ ++match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */ ++ ++match_block.lcc = re->tables + lcc_offset; ++match_block.ctypes = re->tables + ctypes_offset; ++ ++/* The ims options can vary during the matching as a result of the presence ++of (?ims) items in the pattern. They are kept in a local variable so that ++restoring at the exit of a group is easy. */ ++ ++ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); ++ ++/* If the expression has got more back references than the offsets supplied can ++hold, we get a temporary bit of working store to use during the matching. ++Otherwise, we can use the vector supplied, rounding down its size to a multiple ++of 3. */ ++ ++ocount = offsetcount - (offsetcount % 3); ++ ++if (re->top_backref > 0 && re->top_backref >= ocount/3) ++ { ++ ocount = re->top_backref * 3 + 3; ++ match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); ++ if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY; ++ using_temporary_offsets = TRUE; ++ DPRINTF(("Got memory to hold back references\n")); ++ } ++else match_block.offset_vector = offsets; ++ ++match_block.offset_end = ocount; ++match_block.offset_max = (2*ocount)/3; ++match_block.offset_overflow = FALSE; ++ ++/* Compute the minimum number of offsets that we need to reset each time. Doing ++this makes a huge difference to execution time when there aren't many brackets ++in the pattern. */ ++ ++resetcount = 2 + re->top_bracket * 2; ++if (resetcount > offsetcount) resetcount = ocount; ++ ++/* Reset the working variable associated with each extraction. These should ++never be used unless previously set, but they get saved and restored, and so we ++initialize them to avoid reading uninitialized locations. */ ++ ++if (match_block.offset_vector != NULL) ++ { ++ register int *iptr = match_block.offset_vector + ocount; ++ register int *iend = iptr - resetcount/2 + 1; ++ while (--iptr >= iend) *iptr = -1; ++ } ++ ++/* Set up the first character to match, if available. The first_char value is ++never set for an anchored regular expression, but the anchoring may be forced ++at run time, so we have to test for anchoring. The first char may be unset for ++an unanchored pattern, of course. If there's no first char and the pattern was ++studied, there may be a bitmap of possible first characters. */ ++ ++if (!anchored) ++ { ++ if ((re->options & PCRE_FIRSTSET) != 0) ++ { ++ first_char = re->first_char; ++ if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char]; ++ } ++ else ++ if (!startline && extra != NULL && ++ (extra->options & PCRE_STUDY_MAPPED) != 0) ++ start_bits = extra->start_bits; ++ } ++ ++/* For anchored or unanchored matches, there may be a "last known required ++character" set. If the PCRE_CASELESS is set, implying that the match starts ++caselessly, or if there are any changes of this flag within the regex, set up ++both cases of the character. Otherwise set the two values the same, which will ++avoid duplicate testing (which takes significant time). This covers the vast ++majority of cases. It will be suboptimal when the case flag changes in a regex ++and the required character in fact is caseful. */ ++ ++if ((re->options & PCRE_REQCHSET) != 0) ++ { ++ req_char = re->req_char; ++ req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0)? ++ (re->tables + fcc_offset)[req_char] : req_char; ++ } ++ ++/* Loop for handling unanchored repeated matching attempts; for anchored regexs ++the loop runs just once. */ ++ ++do ++ { ++ int rc; ++ register int *iptr = match_block.offset_vector; ++ register int *iend = iptr + resetcount; ++ ++ /* Reset the maximum number of extractions we might see. */ ++ ++ while (iptr < iend) *iptr++ = -1; ++ ++ /* Advance to a unique first char if possible */ ++ ++ if (first_char >= 0) ++ { ++ if ((ims & PCRE_CASELESS) != 0) ++ while (start_match < end_subject && ++ match_block.lcc[*start_match] != first_char) ++ start_match++; ++ else ++ while (start_match < end_subject && *start_match != first_char) ++ start_match++; ++ } ++ ++ /* Or to just after \n for a multiline match if possible */ ++ ++ else if (startline) ++ { ++ if (start_match > match_block.start_subject + start_offset) ++ { ++ while (start_match < end_subject && start_match[-1] != NEWLINE) ++ start_match++; ++ } ++ } ++ ++ /* Or to a non-unique first char after study */ ++ ++ else if (start_bits != NULL) ++ { ++ while (start_match < end_subject) ++ { ++ register int c = *start_match; ++ if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break; ++ } ++ } ++ ++#ifdef DEBUG /* Sigh. Some compilers never learn. */ ++ PCRE_PRINTF(">>>> Match against: "); ++ pchars(start_match, end_subject - start_match, TRUE, &match_block); ++ PCRE_PRINTF("\n"); ++#endif ++ ++ /* If req_char is set, we know that that character must appear in the subject ++ for the match to succeed. If the first character is set, req_char must be ++ later in the subject; otherwise the test starts at the match point. This ++ optimization can save a huge amount of backtracking in patterns with nested ++ unlimited repeats that aren't going to match. We don't know what the state of ++ case matching may be when this character is hit, so test for it in both its ++ cases if necessary. However, the different cased versions will not be set up ++ unless PCRE_CASELESS was given or the casing state changes within the regex. ++ Writing separate code makes it go faster, as does using an autoincrement and ++ backing off on a match. */ ++ ++ if (req_char >= 0) ++ { ++ register const uschar *p = start_match + ((first_char >= 0)? 1 : 0); ++ ++ /* We don't need to repeat the search if we haven't yet reached the ++ place we found it at last time. */ ++ ++ if (p > req_char_ptr) ++ { ++ /* Do a single test if no case difference is set up */ ++ ++ if (req_char == req_char2) ++ { ++ while (p < end_subject) ++ { ++ if (*p++ == req_char) { p--; break; } ++ } ++ } ++ ++ /* Otherwise test for either case */ ++ ++ else ++ { ++ while (p < end_subject) ++ { ++ register int pp = *p++; ++ if (pp == req_char || pp == req_char2) { p--; break; } ++ } ++ } ++ ++ /* If we can't find the required character, break the matching loop */ ++ ++ if (p >= end_subject) break; ++ ++ /* If we have found the required character, save the point where we ++ found it, so that we don't search again next time round the loop if ++ the start hasn't passed this character yet. */ ++ ++ req_char_ptr = p; ++ } ++ } ++ ++ /* When a match occurs, substrings will be set for all internal extractions; ++ we just need to set up the whole thing as substring 0 before returning. If ++ there were too many extractions, set the return code to zero. In the case ++ where we had to get some local store to hold offsets for backreferences, copy ++ those back references that we can. In this case there need not be overflow ++ if certain parts of the pattern were not used. */ ++ ++ match_block.start_match = start_match; ++ if (!match(start_match, re->code, 2, &match_block, ims, NULL, match_isgroup)) ++ continue; ++ ++ /* Copy the offset information from temporary store if necessary */ ++ ++ if (using_temporary_offsets) ++ { ++ if (offsetcount >= 4) ++ { ++ memcpy(offsets + 2, match_block.offset_vector + 2, ++ (offsetcount - 2) * sizeof(int)); ++ DPRINTF(("Copied offsets from temporary memory\n")); ++ } ++ if (match_block.end_offset_top > offsetcount) ++ match_block.offset_overflow = TRUE; ++ ++ DPRINTF(("Freeing temporary memory\n")); ++ (pcre_free)(match_block.offset_vector); ++ } ++ ++ rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2; ++ ++ if (offsetcount < 2) rc = 0; else ++ { ++ offsets[0] = start_match - match_block.start_subject; ++ offsets[1] = match_block.end_match_ptr - match_block.start_subject; ++ } ++ ++ DPRINTF((">>>> returning %d\n", rc)); ++ return rc; ++ } ++ ++/* This "while" is the end of the "do" above */ ++ ++while (!anchored && ++ match_block.errorcode == PCRE_ERROR_NOMATCH && ++ start_match++ < end_subject); ++ ++if (using_temporary_offsets) ++ { ++ DPRINTF(("Freeing temporary memory\n")); ++ (pcre_free)(match_block.offset_vector); ++ } ++ ++DPRINTF((">>>> returning %d\n", match_block.errorcode)); ++ ++return match_block.errorcode; ++} ++ ++/* End of pcre.c */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/aamatch/pcre_exec.h +@@ -0,0 +1,308 @@ ++/* ++ * This is a modified header file containing the definitions from ++ * pcre.h and internal.h required to support pcre_exec() ++ */ ++ ++ ++/************************************************* ++* Perl-Compatible Regular Expressions * ++*************************************************/ ++ ++/* Copyright (c) 1997-2001 University of Cambridge */ ++ ++#ifndef _PCRE_H ++#define _PCRE_H ++ ++/* ----- CODE ADDED ---- */ ++ ++#ifdef __KERNEL__ ++#include // for kmalloc/kfree ++#endif ++ ++#ifdef __KERNEL__ ++#define PCRE_PRINTF printk ++#define isprint(x) ((unsigned char)(x) >= 128 && (unsigned char)(x) <= 255) ++#else ++#define PCRE_PRINTF printf ++#endif ++ ++/* The value of NEWLINE determines the newline character. The default is to ++ * leave it up to the compiler, but some sites want to force a particular value. ++ * On Unix systems, "configure" can be used to override this default. */ ++ ++#ifndef NEWLINE ++#define NEWLINE '\n' ++#endif ++ ++/* ---- CODE DELETED ---- */ ++ ++/* Options */ ++ ++#define PCRE_CASELESS 0x0001 ++#define PCRE_MULTILINE 0x0002 ++#define PCRE_DOTALL 0x0004 ++#define PCRE_EXTENDED 0x0008 ++#define PCRE_ANCHORED 0x0010 ++#define PCRE_DOLLAR_ENDONLY 0x0020 ++#define PCRE_EXTRA 0x0040 ++#define PCRE_NOTBOL 0x0080 ++#define PCRE_NOTEOL 0x0100 ++#define PCRE_UNGREEDY 0x0200 ++#define PCRE_NOTEMPTY 0x0400 ++#define PCRE_UTF8 0x0800 ++ ++/* Exec-time and get-time error codes */ ++ ++#define PCRE_ERROR_NOMATCH (-1) ++#define PCRE_ERROR_NULL (-2) ++#define PCRE_ERROR_BADOPTION (-3) ++#define PCRE_ERROR_BADMAGIC (-4) ++#define PCRE_ERROR_UNKNOWN_NODE (-5) ++#define PCRE_ERROR_NOMEMORY (-6) ++#define PCRE_ERROR_NOSUBSTRING (-7) ++ ++/* ---- CODE DELETED ---- */ ++ ++/* Types */ ++ ++struct real_pcre; /* declaration; the definition is private */ ++struct real_pcre_extra; /* declaration; the definition is private */ ++ ++typedef struct real_pcre pcre; ++typedef struct real_pcre_extra pcre_extra; ++ ++/* ---- CODE DELETED ---- */ ++ ++extern int pcre_exec(const pcre *, const pcre_extra *, ++ const char *, int, int, int, int *, ++ int); ++ ++/* ---- CODE ADDED (from internal.h) ---- */ ++ ++/* These are the public options that can change during matching. */ ++ ++#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) ++ ++/* Private options flags start at the most significant end of the four bytes, ++but skip the top bit so we can use ints for convenience without getting tangled ++with negative values. The public options defined in pcre.h start at the least ++significant end. Make sure they don't overlap, though now that we have expanded ++to four bytes there is plenty of space. */ ++ ++#define PCRE_FIRSTSET 0x40000000 /* first_char is set */ ++#define PCRE_REQCHSET 0x20000000 /* req_char is set */ ++#define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */ ++#define PCRE_ICHANGED 0x04000000 /* i option changes within regex */ ++ ++/* Options for the "extra" block produced by pcre_study(). */ ++ ++#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ ++ ++/* Masks for identifying the public options which are permitted at compile ++time, run time or study time, respectively. */ ++ ++#define PUBLIC_EXEC_OPTIONS \ ++ (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY) ++ ++/* Magic number to provide a small check against being handed junk. */ ++ ++#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ ++ ++typedef int BOOL; ++ ++#define FALSE 0 ++#define TRUE 1 ++ ++/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets ++that extract substrings. Starting from 1 (i.e. after OP_END), the values up to ++OP_EOD must correspond in order to the list of escapes immediately above. */ ++ ++enum { ++ OP_END, /* End of pattern */ ++ ++ /* Values corresponding to backslashed metacharacters */ ++ ++ OP_SOD, /* Start of data: \A */ ++ OP_NOT_WORD_BOUNDARY, /* \B */ ++ OP_WORD_BOUNDARY, /* \b */ ++ OP_NOT_DIGIT, /* \D */ ++ OP_DIGIT, /* \d */ ++ OP_NOT_WHITESPACE, /* \S */ ++ OP_WHITESPACE, /* \s */ ++ OP_NOT_WORDCHAR, /* \W */ ++ OP_WORDCHAR, /* \w */ ++ OP_EODN, /* End of data or \n at end of data: \Z. */ ++ OP_EOD, /* End of data: \z */ ++ ++ OP_OPT, /* Set runtime options */ ++ OP_CIRC, /* Start of line - varies with multiline switch */ ++ OP_DOLL, /* End of line - varies with multiline switch */ ++ OP_ANY, /* Match any character */ ++ OP_CHARS, /* Match string of characters */ ++ OP_NOT, /* Match anything but the following char */ ++ ++ OP_STAR, /* The maximizing and minimizing versions of */ ++ OP_MINSTAR, /* all these opcodes must come in pairs, with */ ++ OP_PLUS, /* the minimizing one second. */ ++ OP_MINPLUS, /* This first set applies to single characters */ ++ OP_QUERY, ++ OP_MINQUERY, ++ OP_UPTO, /* From 0 to n matches */ ++ OP_MINUPTO, ++ OP_EXACT, /* Exactly n matches */ ++ ++ OP_NOTSTAR, /* The maximizing and minimizing versions of */ ++ OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */ ++ OP_NOTPLUS, /* the minimizing one second. */ ++ OP_NOTMINPLUS, /* This first set applies to "not" single characters */ ++ OP_NOTQUERY, ++ OP_NOTMINQUERY, ++ OP_NOTUPTO, /* From 0 to n matches */ ++ OP_NOTMINUPTO, ++ OP_NOTEXACT, /* Exactly n matches */ ++ ++ OP_TYPESTAR, /* The maximizing and minimizing versions of */ ++ OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */ ++ OP_TYPEPLUS, /* the minimizing one second. These codes must */ ++ OP_TYPEMINPLUS, /* be in exactly the same order as those above. */ ++ OP_TYPEQUERY, /* This set applies to character types such as \d */ ++ OP_TYPEMINQUERY, ++ OP_TYPEUPTO, /* From 0 to n matches */ ++ OP_TYPEMINUPTO, ++ OP_TYPEEXACT, /* Exactly n matches */ ++ ++ OP_CRSTAR, /* The maximizing and minimizing versions of */ ++ OP_CRMINSTAR, /* all these opcodes must come in pairs, with */ ++ OP_CRPLUS, /* the minimizing one second. These codes must */ ++ OP_CRMINPLUS, /* be in exactly the same order as those above. */ ++ OP_CRQUERY, /* These are for character classes and back refs */ ++ OP_CRMINQUERY, ++ OP_CRRANGE, /* These are different to the three seta above. */ ++ OP_CRMINRANGE, ++ ++ OP_CLASS, /* Match a character class */ ++ OP_REF, /* Match a back reference */ ++ OP_RECURSE, /* Match this pattern recursively */ ++ ++ OP_ALT, /* Start of alternation */ ++ OP_KET, /* End of group that doesn't have an unbounded repeat */ ++ OP_KETRMAX, /* These two must remain together and in this */ ++ OP_KETRMIN, /* order. They are for groups the repeat for ever. */ ++ ++ /* The assertions must come before ONCE and COND */ ++ ++ OP_ASSERT, /* Positive lookahead */ ++ OP_ASSERT_NOT, /* Negative lookahead */ ++ OP_ASSERTBACK, /* Positive lookbehind */ ++ OP_ASSERTBACK_NOT, /* Negative lookbehind */ ++ OP_REVERSE, /* Move pointer back - used in lookbehind assertions */ ++ ++ /* ONCE and COND must come after the assertions, with ONCE first, as there's ++ a test for >= ONCE for a subpattern that isn't an assertion. */ ++ ++ OP_ONCE, /* Once matched, don't back up into the subpattern */ ++ OP_COND, /* Conditional group */ ++ OP_CREF, /* Used to hold an extraction string number (cond ref) */ ++ ++ OP_BRAZERO, /* These two must remain together and in this */ ++ OP_BRAMINZERO, /* order. */ ++ ++ OP_BRANUMBER, /* Used for extracting brackets whose number is greater ++ than can fit into an opcode. */ ++ ++ OP_BRA /* This and greater values are used for brackets that ++ extract substrings up to a basic limit. After that, ++ use is made of OP_BRANUMBER. */ ++}; ++ ++/* The highest extraction number before we have to start using additional ++bytes. (Originally PCRE didn't have support for extraction counts highter than ++this number.) The value is limited by the number of opcodes left after OP_BRA, ++i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional ++opcodes. */ ++ ++#define EXTRACT_BASIC_MAX 150 ++ ++/* All character handling must be done as unsigned characters. Otherwise there ++are problems with top-bit-set characters and functions such as isspace(). ++However, we leave the interface to the outside world as char *, because that ++should make things easier for callers. We define a short type for unsigned char ++to save lots of typing. I tried "uchar", but it causes problems on Digital ++Unix, where it is defined in sys/types, so use "uschar" instead. */ ++ ++typedef unsigned char uschar; ++ ++/* The real format of the start of the pcre block; the actual code vector ++runs on as long as necessary after the end. */ ++ ++typedef struct real_pcre { ++ unsigned long int magic_number; ++ size_t size; ++ const unsigned char *tables; ++ unsigned long int options; ++ unsigned short int top_bracket; ++ unsigned short int top_backref; ++ uschar first_char; ++ uschar req_char; ++ uschar code[1]; ++} real_pcre; ++ ++/* The real format of the extra block returned by pcre_study(). */ ++ ++typedef struct real_pcre_extra { ++ uschar options; ++ uschar start_bits[32]; ++} real_pcre_extra; ++ ++/* Structure for passing "static" information around between the functions ++doing the matching, so that they are thread-safe. */ ++ ++typedef struct match_data { ++ int errorcode; /* As it says */ ++ int *offset_vector; /* Offset vector */ ++ int offset_end; /* One past the end */ ++ int offset_max; /* The maximum usable for return data */ ++ const uschar *lcc; /* Points to lower casing table */ ++ const uschar *ctypes; /* Points to table of type maps */ ++ BOOL offset_overflow; /* Set if too many extractions */ ++ BOOL notbol; /* NOTBOL flag */ ++ BOOL noteol; /* NOTEOL flag */ ++ BOOL utf8; /* UTF8 flag */ ++ BOOL endonly; /* Dollar not before final \n */ ++ BOOL notempty; /* Empty string match not wanted */ ++ const uschar *start_pattern; /* For use when recursing */ ++ const uschar *start_subject; /* Start of the subject string */ ++ const uschar *end_subject; /* End of the subject string */ ++ const uschar *start_match; /* Start of this match attempt */ ++ const uschar *end_match_ptr; /* Subject position at end match */ ++ int end_offset_top; /* Highwater mark at end of match */ ++} match_data; ++ ++/* Bit definitions for entries in the pcre_ctypes table. */ ++ ++#define ctype_space 0x01 ++#define ctype_letter 0x02 ++#define ctype_digit 0x04 ++#define ctype_xdigit 0x08 ++#define ctype_word 0x10 /* alphameric or '_' */ ++#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ ++ ++/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set ++of bits for a class map. Some classes are built by combining these tables. */ ++ ++#define cbit_length 320 /* Length of the cbits table */ ++ ++/* Offsets of the various tables from the base tables pointer, and ++total length. */ ++ ++#define lcc_offset 0 ++#define fcc_offset 256 ++ ++#define fcc_offset 256 ++#define cbits_offset 512 ++#define ctypes_offset (cbits_offset + cbit_length) ++ ++/* ----- CODE ADDED ---- */ ++ ++#endif // _PCRE_H ++ /* End of pcre.h */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/aamatch/pcre_tables.h +@@ -0,0 +1,184 @@ ++ ++/************************************************* ++* Perl-Compatible Regular Expressions * ++*************************************************/ ++ ++/* This file is automatically written by the dftables auxiliary ++program. If you edit it by hand, you might like to edit the Makefile to ++prevent its ever being regenerated. ++ ++This file is #included in the compilation of pcre.c to build the default ++character tables which are used when no tables are passed to the compile ++function. */ ++ ++static unsigned char pcre_default_tables[] = { ++ ++/* This table is a lower casing table. */ ++ ++ 0, 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, 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, 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, ++ ++/* This table is a case flipping table. */ ++ ++ 0, 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, 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, 91, 92, 93, 94, 95, ++ 96, 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,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, ++ ++/* This table contains bit maps for various character classes. ++Each map is 32 bytes long and the bits run from the least ++significant end of each byte. The classes that have their own ++maps are: space, xdigit, digit, upper, lower, word, graph ++print, punct, and cntrl. Other classes are built from combinations. */ ++ ++ 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, ++ 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, ++ 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, ++ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, ++ 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++ ++/* This table identifies various classes of character by individual bits: ++ 0x01 white space character ++ 0x02 letter ++ 0x04 decimal digit ++ 0x08 hexadecimal digit ++ 0x10 alphanumeric or '_' ++ 0x80 regular expression metacharacter or binary zero ++*/ ++ ++ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ ++ 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ ++ 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ ++ 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ ++ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ ++ 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ ++ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ ++ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ ++ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ ++ 0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /* X - _ */ ++ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ ++ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ ++ 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ ++ 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ ++ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ ++ ++/* End of chartables.c */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/Kconfig +@@ -0,0 +1,9 @@ ++config SECURITY_APPARMOR ++ tristate "AppArmor support" ++ depends on SECURITY!=n ++ help ++ This enables the Novell AppArmor security module. ++ Required userspace tools (if they are not included in your ++ distribution) and further information may be found at ++ ++ If you are unsure how to answer this question, answer N. +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/aamatch/Makefile +@@ -0,0 +1,5 @@ ++# Makefile for AppArmor aamatch submodule ++# ++obj-$(CONFIG_SECURITY_APPARMOR) += aamatch_pcre.o ++ ++aamatch_pcre-y := match_pcre.o pcre_exec.o +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/apparmor.h +@@ -0,0 +1,283 @@ ++/* ++ * Copyright (C) 1998-2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor internal prototypes ++ */ ++ ++#ifndef __SUBDOMAIN_H ++#define __SUBDOMAIN_H ++ ++/* defn of iattr */ ++#include ++ ++#include "shared.h" ++ ++/* Control parameters (0 or 1), settable thru module/boot flags or ++ * via /sys/kernel/security/subdomain/control */ ++extern int subdomain_complain; ++extern int subdomain_debug; ++extern int subdomain_audit; ++extern int subdomain_logsyscall; ++ ++#define SD_UNCONSTRAINED "unconstrained" ++ ++/* $ echo -n subdomain.o | md5sum | cut -c -8 */ ++#define SD_ID_MAGIC 0x8c235e38 ++ ++#define PROFILE_COMPLAIN(_profile) \ ++ (subdomain_complain == 1 || ((_profile) && (_profile)->flags.complain)) ++ ++#define SUBDOMAIN_COMPLAIN(_sd) \ ++ (subdomain_complain == 1 || \ ++ ((_sd) && (_sd)->active && (_sd)->active->flags.complain)) ++ ++#define SUBDOMAIN_AUDIT(_sd) \ ++ (subdomain_audit == 1 || \ ++ ((_sd) && (_sd)->active && (_sd)->active->flags.audit)) ++ ++/* ++ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl ++ * which is not related to profile accesses. ++ */ ++ ++#define SD_DEBUG(fmt, args...) \ ++ do { \ ++ if (subdomain_debug) \ ++ printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ ++ } while (0) ++#define SD_INFO(fmt, args...) printk(KERN_INFO "AppArmor: " fmt, ##args) ++#define SD_WARN(fmt, args...) printk(KERN_WARNING "AppArmor: " fmt, ##args) ++#define SD_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args) ++ ++/* basic AppArmor data structures */ ++ ++struct flagval { ++ int debug; ++ int complain; ++ int audit; ++}; ++ ++enum entry_t { ++ sd_entry_literal, ++ sd_entry_tailglob, ++ sd_entry_pattern, ++ sd_entry_invalid ++}; ++ ++/** ++ * sd_entry - file ACL * ++ * Each entry describes a file and an allowed access mode. ++ */ ++struct sd_entry { ++ char *filename; ++ int mode; /* mode is 'or' of READ, WRITE, EXECUTE, ++ * INHERIT, UNCONSTRAINED, and LIBRARY ++ * (meaning don't prefetch). */ ++ ++ enum entry_t entry_type; ++ void *extradata; ++ ++ struct list_head list; ++ struct list_head listp[POS_SD_FILE_MAX + 1]; ++}; ++ ++#define SD_EXEC_MODIFIER_MASK(mask) ((mask) & (SD_EXEC_UNCONSTRAINED |\ ++ SD_EXEC_INHERIT |\ ++ SD_EXEC_PROFILE)) ++ ++#define SD_EXEC_MASK(mask) ((mask) & (SD_MAY_EXEC |\ ++ SD_EXEC_UNCONSTRAINED |\ ++ SD_EXEC_INHERIT |\ ++ SD_EXEC_PROFILE)) ++ ++/** ++ * sdprofile - basic confinement data ++ * ++ * The AppArmor profile contains the basic confinement data. Each profile ++ * has a name and potentially a list of subdomain entries. The profiles are ++ * connected in a list ++ */ ++struct sdprofile { ++ char *name; /* profile name */ ++ ++ struct list_head file_entry; /* file ACL */ ++ struct list_head file_entryp[POS_SD_FILE_MAX + 1]; ++ struct list_head list; /* list of profiles */ ++ struct list_head sub; /* sub profiles, for change_hat */ ++ struct flagval flags; /* per profile debug flags */ ++ ++ int isstale; /* is profile stale */ ++ ++ int num_file_entries; ++ int num_file_pentries[POS_SD_FILE_MAX + 1]; ++ ++ kernel_cap_t capabilities; ++ ++ atomic_t count; /* reference count */ ++}; ++ ++/** ++ * subdomain - a task's subdomain ++ * ++ * Contains the original profile obtained from execve() as well as the ++ * current active profile (which could change due to change_hat). Plus ++ * the hat_magic needed during change_hat. ++ */ ++struct subdomain { ++ __u32 sd_magic; /* magic value to distinguish blobs */ ++ struct sdprofile *profile; /* The profile obtained from execve() */ ++ struct sdprofile *active; /* The current active profile */ ++ __u32 sd_hat_magic; /* used with change_hat */ ++ struct list_head list; /* list of subdomains */ ++ struct task_struct *task; ++}; ++ ++typedef int (*sd_iter) (struct subdomain *, void *); ++ ++/* sd_path_data ++ * temp (cookie) data used by sd_path_* functions, see inline.h ++ */ ++struct sd_path_data { ++ struct dentry *root, *dentry; ++ struct namespace *namespace; ++ struct list_head *head, *pos; ++ int errno; ++}; ++ ++#define SD_SUBDOMAIN(sec) ((struct subdomain*)(sec)) ++#define SD_PROFILE(sec) ((struct sdprofile*)(sec)) ++ ++/* Lock protecting access to 'struct subdomain' accesses */ ++extern rwlock_t sd_lock; ++ ++extern struct sdprofile *null_profile; ++extern struct sdprofile *null_complain_profile; ++ ++/** sd_audit ++ * ++ * Auditing structure ++ */ ++ ++struct sd_audit { ++ unsigned short type, flags; ++ unsigned int result; ++ unsigned int gfp_mask; ++ int errorcode; ++ ++ const char *name; ++ unsigned int ival; ++ union{ ++ const void *pval; ++ va_list vaval; ++ }; ++}; ++ ++/* audit types */ ++#define SD_AUDITTYPE_FILE 1 ++#define SD_AUDITTYPE_DIR 2 ++#define SD_AUDITTYPE_ATTR 3 ++#define SD_AUDITTYPE_XATTR 4 ++#define SD_AUDITTYPE_LINK 5 ++#define SD_AUDITTYPE_CAP 6 ++#define SD_AUDITTYPE_MSG 7 ++#define SD_AUDITTYPE_SYSCALL 8 ++#define SD_AUDITTYPE__END 9 ++ ++/* audit flags */ ++#define SD_AUDITFLAG_AUDITSS_SYSCALL 1 /* log syscall context */ ++#define SD_AUDITFLAG_LOGERR 2 /* log operations that failed due to ++ non permission errors */ ++ ++#define HINT_UNKNOWN_HAT "unknown_hat" ++#define HINT_FORK "fork" ++#define HINT_MANDPROF "missing_mandatory_profile" ++#define HINT_CHGPROF "changing_profile" ++ ++#define LOG_HINT(sd, gfp, hint, fmt, args...) \ ++ do {\ ++ sd_audit_message(sd, gfp, 0, \ ++ "LOGPROF-HINT " hint " " fmt, ##args);\ ++ } while(0) ++ ++/* diroptype */ ++#define SD_DIR_MKDIR 0 ++#define SD_DIR_RMDIR 1 ++ ++/* xattroptype */ ++#define SD_XATTR_GET 0 ++#define SD_XATTR_SET 1 ++#define SD_XATTR_LIST 2 ++#define SD_XATTR_REMOVE 3 ++ ++/* main.c */ ++extern int alloc_nullprofiles(void); ++extern void free_nullprofiles(void); ++extern int sd_audit_message(struct subdomain *, unsigned int gfp, int, ++ const char *, ...); ++extern int sd_audit_syscallreject(struct subdomain *, unsigned int gfp, ++ const char *); ++extern int sd_audit(struct subdomain *, const struct sd_audit *); ++extern char *sd_get_name(struct dentry *dentry, struct vfsmount *mnt); ++ ++extern int sd_attr(struct subdomain *sd, struct dentry *dentry, ++ struct iattr *iattr); ++extern int sd_xattr(struct subdomain *sd, struct dentry *dentry, ++ const char *xattr, int xattroptype); ++extern int sd_capability(struct subdomain *sd, int cap); ++extern int sd_perm(struct subdomain *sd, struct dentry *dentry, ++ struct vfsmount *mnt, int mask); ++extern int sd_perm_nameidata(struct subdomain *sd, struct nameidata *nd, ++ int mask); ++extern int sd_perm_dentry(struct subdomain *sd, struct dentry *dentry, ++ int mask); ++extern int sd_perm_dir(struct subdomain *sd, struct dentry *dentry, ++ int diroptype); ++extern int sd_link(struct subdomain *sd, ++ struct dentry *link, struct dentry *target); ++extern int sd_fork(struct task_struct *p); ++extern int sd_register(struct file *file); ++extern void sd_release(struct task_struct *p); ++extern int sd_change_hat(const char *id, __u32 hat_magic); ++extern int sd_associate_filp(struct file *filp); ++ ++/* list.c */ ++extern struct sdprofile *sd_profilelist_find(const char *name); ++extern int sd_profilelist_add(struct sdprofile *profile); ++extern int sd_profilelist_remove(const char *name); ++extern void sd_profilelist_release(void); ++extern struct sdprofile *sd_profilelist_replace(struct sdprofile *profile); ++extern void sd_profile_dump(struct sdprofile *); ++extern void sd_profilelist_dump(void); ++extern void sd_subdomainlist_add(struct subdomain *); ++extern void sd_subdomainlist_remove(struct subdomain *); ++extern void sd_subdomainlist_iterate(sd_iter, void *); ++extern void sd_subdomainlist_iterateremove(sd_iter, void *); ++extern void sd_subdomainlist_release(void); ++ ++/* subdomain_interface.c */ ++extern void free_sdprofile(struct sdprofile *profile); ++extern int sd_sys_security(unsigned int id, unsigned call, unsigned long *args); ++ ++/* procattr.c */ ++extern size_t sd_getprocattr(struct subdomain *sd, char *str, size_t size); ++extern int sd_setprocattr_changehat(char *hatinfo, size_t infosize); ++extern int sd_setprocattr_setprofile(struct task_struct *p, char *profilename, ++ size_t profilesize); ++ ++/* apparmorfs.c */ ++extern int create_subdomainfs(void); ++extern int destroy_subdomainfs(void); ++ ++/* capabilities.c */ ++extern const char *capability_to_name(unsigned int cap); ++ ++/* apparmor_version.c */ ++extern const char *apparmor_version(void); ++extern const char *apparmor_version_nl(void); ++ ++#endif /* __SUBDOMAIN_H */ +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/apparmor_version.c +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor version definition ++ */ ++ ++#ifndef APPARMOR_VERSION ++#error "-DAPPARMOR_VERSION must be specified when compiling this file" ++#endif ++ ++#define APPARMOR_VERSION_STR_PFX "APPARMOR_VERSION=" ++ ++#include ++MODULE_VERSION(APPARMOR_VERSION); ++ ++/* apparmor_version_str exists to allow a strings on module to ++ * see APPARMOR_VERSION= prefix ++ */ ++static const char *apparmor_version_str = ++ APPARMOR_VERSION_STR_PFX APPARMOR_VERSION; ++ ++/* apparmor_version_str_nl exists to allow an easy way to get a newline ++ * terminated string without having to do dynamic memory allocation ++ */ ++static const char *apparmor_version_str_nl = APPARMOR_VERSION "\n"; ++ ++const char *apparmor_version(void) ++{ ++ const int len = sizeof(APPARMOR_VERSION_STR_PFX) - 1; ++ ++ return apparmor_version_str + len; ++} ++ ++const char *apparmor_version_nl(void) ++{ ++ return apparmor_version_str_nl; ++} +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/apparmorfs.c +@@ -0,0 +1,417 @@ ++/* ++ * Copyright (C) 2005 Novell/SUSE ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * AppArmor filesystem (part of securityfs) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "apparmor.h" ++#include "inline.h" ++ ++#define SECFS_SD "apparmor" ++static struct dentry *sdfs_dentry = NULL; ++ ++/* profile */ ++extern struct seq_operations subdomainfs_profiles_op; ++static int sd_prof_open(struct inode *inode, struct file *file); ++static int sd_prof_release(struct inode *inode, struct file *file); ++ ++static struct file_operations subdomainfs_profiles_fops = { ++ .open = sd_prof_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = sd_prof_release, ++}; ++ ++/* version */ ++static ssize_t sd_version_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos); ++ ++static struct file_operations subdomainfs_version_fops = { ++ .read = sd_version_read, ++}; ++ ++/* interface */ ++extern ssize_t sd_file_prof_add(void *, size_t); ++extern ssize_t sd_file_prof_repl(void *, size_t); ++extern ssize_t sd_file_prof_remove(const char *, int); ++ ++static ssize_t sd_profile_load(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos); ++static ssize_t sd_profile_replace(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos); ++static ssize_t sd_profile_remove(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos); ++ ++static struct file_operations subdomainfs_profile_load = { ++ .write = sd_profile_load ++}; ++ ++static struct file_operations subdomainfs_profile_replace = { ++ .write = sd_profile_replace ++}; ++ ++static struct file_operations subdomainfs_profile_remove = { ++ .write = sd_profile_remove ++}; ++ ++ ++/* control */ ++static u64 sd_control_get(void *data); ++static void sd_control_set(void *data, u64 val); ++ ++DEFINE_SIMPLE_ATTRIBUTE(subdomainfs_control_fops, sd_control_get, ++ sd_control_set, "%lld\n"); ++ ++ ++ ++/* table of static entries */ ++ ++static struct root_entry { ++ const char *name; ++ int mode; ++ int access; ++ struct file_operations *fops; ++ void *data; ++ ++ /* internal fields */ ++ struct dentry *dentry; ++ int parent_index; ++} root_entries[] = { ++ /* our root, normally /sys/kernel/security/subdomain */ ++ {SECFS_SD, S_IFDIR, 0550}, /* DO NOT EDIT/MOVE */ ++ ++ /* interface for obtaining list of profiles currently loaded */ ++ {"profiles", S_IFREG, 0440, &subdomainfs_profiles_fops, ++ NULL}, ++ ++ /* interface for obtaining version# of subdomain */ ++ {"version", S_IFREG, 0440, &subdomainfs_version_fops, ++ NULL}, ++ ++ /* interface for loading/removing/replacing profiles */ ++ {".load", S_IFREG, 0640, &subdomainfs_profile_load, ++ NULL}, ++ {".replace", S_IFREG, 0640, &subdomainfs_profile_replace, ++ NULL}, ++ {".remove", S_IFREG, 0640, &subdomainfs_profile_remove, ++ NULL}, ++ ++ /* interface for setting binary config values */ ++ {"control", S_IFDIR, 0550}, ++ {"complain", S_IFREG, 0640, &subdomainfs_control_fops, ++ &subdomain_complain}, ++ {"audit", S_IFREG, 0640, &subdomainfs_control_fops, ++ &subdomain_audit}, ++ {"debug", S_IFREG, 0640, &subdomainfs_control_fops, ++ &subdomain_debug}, ++ {"logsyscall", S_IFREG, 0640, &subdomainfs_control_fops, ++ &subdomain_logsyscall}, ++ {NULL, S_IFDIR, 0}, ++ ++ /* root end */ ++ {NULL, S_IFDIR, 0} ++}; ++ ++#define SDFS_DENTRY root_entries[0].dentry ++ ++static const unsigned int num_entries = ++ sizeof(root_entries) / sizeof(struct root_entry); ++ ++ ++ ++static int sd_prof_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &subdomainfs_profiles_op); ++} ++ ++ ++static int sd_prof_release(struct inode *inode, struct file *file) ++{ ++ return seq_release(inode, file); ++} ++ ++static ssize_t sd_version_read(struct file *file, char __user *buf, ++ size_t size, loff_t *ppos) ++{ ++ const char *version = apparmor_version_nl(); ++ ++ return simple_read_from_buffer(buf, size, ppos, version, ++ strlen(version)); ++} ++ ++static char *sd_simple_write_to_buffer(const char __user *userbuf, ++ size_t alloc_size, size_t copy_size, ++ loff_t *pos, const char *msg) ++{ ++ char *data; ++ ++ if (*pos != 0) { ++ /* only writes from pos 0, that is complete writes */ ++ data = ERR_PTR(-ESPIPE); ++ goto out; ++ } ++ ++ /* Don't allow confined processes to load/replace/remove profiles. ++ * No sane person would add rules allowing this to a profile ++ * but we enforce the restriction anyways. ++ */ ++ if (sd_is_confined()) { ++ struct subdomain *sd = SD_SUBDOMAIN(current->security); ++ ++ SD_WARN("REJECTING access to profile %s (%s(%d) " ++ "profile %s active %s)\n", ++ msg, current->comm, current->pid, ++ sd->profile->name, sd->active->name); ++ ++ data = ERR_PTR(-EPERM); ++ goto out; ++ } ++ ++ data = vmalloc(alloc_size); ++ if (data == NULL) { ++ data = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ if (copy_from_user(data, userbuf, copy_size)) { ++ vfree(data); ++ data = ERR_PTR(-EFAULT); ++ goto out; ++ } ++ ++out: ++ return data; ++} ++ ++static ssize_t sd_profile_load(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ data = sd_simple_write_to_buffer(buf, size, size, pos, "load"); ++ ++ if (!IS_ERR(data)) { ++ error = sd_file_prof_add(data, size); ++ vfree(data); ++ } else { ++ error = PTR_ERR(data); ++ } ++ ++ return error; ++} ++ ++static ssize_t sd_profile_replace(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ data = sd_simple_write_to_buffer(buf, size, size, pos, "replacement"); ++ ++ if (!IS_ERR(data)) { ++ error = sd_file_prof_repl(data, size); ++ vfree(data); ++ } else { ++ error = PTR_ERR(data); ++ } ++ ++ return error; ++} ++ ++static ssize_t sd_profile_remove(struct file *f, const char __user *buf, ++ size_t size, loff_t *pos) ++{ ++ char *data; ++ ssize_t error; ++ ++ /* sd_file_prof_remove needs a null terminated string so 1 extra ++ * byte is allocated and null the copied data is then null terminated ++ */ ++ data = sd_simple_write_to_buffer(buf, size+1, size, pos, "removal"); ++ ++ if (!IS_ERR(data)) { ++ data[size] = 0; ++ error = sd_file_prof_remove(data, size); ++ vfree(data); ++ } else { ++ error = PTR_ERR(data); ++ } ++ ++ return error; ++} ++ ++static u64 sd_control_get(void *data) ++{ ++ return *(int *)data; ++} ++ ++static void sd_control_set(void *data, u64 val) ++{ ++ if (val > 1) ++ val = 1; ++ ++ *(int*)data = (int)val; ++} ++ ++static void clear_subdomainfs(void) ++{ ++ unsigned int i; ++ ++ for (i=0; i < num_entries;i++) { ++ unsigned int index; ++ ++ if (root_entries[i].mode == S_IFDIR) { ++ if (root_entries[i].name) ++ /* defer dir free till all sub-entries freed */ ++ continue; ++ else ++ /* cleanup parent */ ++ index = root_entries[i].parent_index; ++ } else { ++ index = i; ++ } ++ ++ if (root_entries[index].dentry) { ++ securityfs_remove(root_entries[index].dentry); ++ ++ SD_DEBUG("%s: deleted subdomainfs entry name=%s " ++ "dentry=%p\n", ++ __FUNCTION__, ++ root_entries[index].name, ++ root_entries[index].dentry); ++ ++ root_entries[index].dentry = NULL; ++ root_entries[index].parent_index = 0; ++ } ++ } ++} ++ ++static int populate_subdomainfs(struct dentry *root) ++{ ++ unsigned int i, parent_index, depth; ++ ++#define ENT root_entries[i] ++ ++ for (i = 0; i < num_entries; i++) { ++ root_entries[i].dentry = NULL; ++ root_entries[i].parent_index = 0; ++ } ++ ++ /* 1. Verify entry 0 is valid [sanity check] */ ++ if (num_entries == 0 || ++ !root_entries[0].name || ++ strcmp(root_entries[0].name, SECFS_SD) != 0 || ++ root_entries[0].mode != S_IFDIR) { ++ SD_ERROR("%s: root entry 0 is not SECFS_SD/dir\n", ++ __FUNCTION__); ++ goto error; ++ } ++ ++ /* 2. Verify table structure */ ++ parent_index = 0; ++ depth = 1; ++ ++ for (i = 1; i < num_entries; i++) { ++ ENT.parent_index = parent_index; ++ ++ if (ENT.name && ENT.mode == S_IFDIR) { ++ depth++; ++ parent_index = i; ++ } else if (!ENT.name) { ++ if (ENT.mode != S_IFDIR || depth == 0) { ++ SD_ERROR("%s: root_entry %d invalid (%u %d)", ++ __FUNCTION__, i, ++ ENT.mode, ENT.parent_index); ++ goto error; ++ } ++ ++ depth--; ++ parent_index = root_entries[parent_index].parent_index; ++ } ++ } ++ ++ if (depth != 0) { ++ SD_ERROR("%s: root_entry table not correctly terminated\n", ++ __FUNCTION__); ++ goto error; ++ } ++ ++ /* 3. Create root (parent=NULL) */ ++ i=0; ++ ++ ENT.dentry = securityfs_create_file(ENT.name, ++ ENT.mode | ENT.access, ++ NULL, NULL, NULL); ++ ++ if (ENT.dentry) ++ SD_DEBUG("%s: created securityfs/subdomain [dentry=%p]\n", ++ __FUNCTION__, ENT.dentry); ++ else ++ goto error; ++ ++ ++ /* 4. create remaining nodes */ ++ for (i = 1; i < num_entries; i++) { ++ struct dentry *parent; ++ ++ /* end of directory ? */ ++ if (!ENT.name) ++ continue; ++ ++ parent = root_entries[ENT.parent_index].dentry; ++ ++ ENT.dentry = securityfs_create_file(ENT.name, ++ ENT.mode | ENT.access, ++ parent, ++ ENT.mode != S_IFDIR ? ENT.data : NULL, ++ ENT.mode != S_IFDIR ? ENT.fops : NULL); ++ ++ if (!ENT.dentry) ++ goto cleanup_error; ++ ++ SD_DEBUG("%s: added subdomainfs entry " ++ "name=%s mode=%x dentry=%p [parent %p]\n", ++ __FUNCTION__, ENT.name, ENT.mode|ENT.access, ++ ENT.dentry, parent); ++ } ++ ++ return 1; ++ ++cleanup_error: ++ clear_subdomainfs(); ++ ++error: ++ return 0; ++} ++ ++int create_subdomainfs(void) ++{ ++ if (SDFS_DENTRY) ++ SD_ERROR("%s: Subdomain securityfs already exists\n", ++ __FUNCTION__); ++ else if (!populate_subdomainfs(sdfs_dentry)) ++ SD_ERROR("%s: Error populating Subdomain securityfs\n", ++ __FUNCTION__); ++ ++ return (SDFS_DENTRY != NULL); ++} ++ ++int destroy_subdomainfs(void) ++{ ++ if (SDFS_DENTRY) ++ clear_subdomainfs(); ++ ++ return 1; ++} +--- /dev/null ++++ linux-2.6.16-SL101_BRANCH/security/apparmor/shared.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2000, 2001, 2004, 2005 Novell/SUSE ++ * ++ * Immunix AppArmor LSM ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef _SHARED_H ++#define _SHARED_H ++ ++/* start of system offsets */ ++#define POS_SD_FILE_MIN 0 ++#define POS_SD_MAY_EXEC POS_SD_FILE_MIN ++#define POS_SD_MAY_WRITE (POS_SD_MAY_EXEC + 1) ++#define POS_SD_MAY_READ (POS_SD_MAY_WRITE + 1) ++/* not used by Subdomain */ ++#define POS_SD_MAY_APPEND (POS_SD_MAY_READ + 1) ++/* end of system offsets */ ++ ++#define POS_SD_MAY_LINK (POS_SD_MAY_APPEND + 1) ++#define POS_SD_EXEC_INHERIT (POS_SD_MAY_LINK + 1) ++#define POS_SD_EXEC_UNCONSTRAINED (POS_SD_EXEC_INHERIT + 1) ++#define POS_SD_EXEC_PROFILE (POS_SD_EXEC_UNCONSTRAINED + 1) ++#define POS_SD_FILE_MAX POS_SD_EXEC_PROFILE ++ ++/* Modeled after MAY_READ, MAY_WRITE, MAY_EXEC def'ns */ ++#define SD_MAY_EXEC (0x01 << POS_SD_MAY_EXEC) ++#define SD_MAY_WRITE (0x01 << POS_SD_MAY_WRITE) ++#define SD_MAY_READ (0x01 << POS_SD_MAY_READ) ++#define SD_MAY_LINK (0x01 << POS_SD_MAY_LINK) ++#define SD_EXEC_INHERIT (0x01 << POS_SD_EXEC_INHERIT) ++#define SD_EXEC_UNCONSTRAINED (0x01 << POS_SD_EXEC_UNCONSTRAINED) ++#define SD_EXEC_PROFILE (0x01 << POS_SD_EXEC_PROFILE) ++#define SD_EXEC_MODIFIERS(X) (X & (SD_EXEC_INHERIT | \ ++ SD_EXEC_UNCONSTRAINED | \ ++ SD_EXEC_PROFILE)) ++ ++#endif /* _SHARED_H */ diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch new file mode 100644 index 0000000..1dd1f54 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/apparmor-sources-2.6.17-r1-apparmor_namespacesem.patch @@ -0,0 +1,36 @@ +Subject: Export namespace semaphore +Patch-mainline: no + +Export global namespace_sem (this used to be a per namespace semaphore). +Alas, this isn't going to win _any_ points for style. +Patch is not in mainline -- pending AppArmor code submission to lkml + +Index: linux-2.6.17/fs/namespace.c +=================================================================== +--- linux-2.6.17.orig/fs/namespace.c ++++ linux-2.6.17/fs/namespace.c +@@ -46,7 +46,8 @@ + static struct list_head *mount_hashtable __read_mostly; + static int hash_mask __read_mostly, hash_bits __read_mostly; + static kmem_cache_t *mnt_cache __read_mostly; +-static struct rw_semaphore namespace_sem; ++struct rw_semaphore namespace_sem; ++EXPORT_SYMBOL_GPL(namespace_sem); + + /* /sys/fs */ + decl_subsys(fs, NULL, NULL); + +Index: linux-2.6.17/include/linux/namespace.h +=================================================================== +--- linux-2.6.17.orig/include/linux/namespace.h ++++ linux-2.6.17/include/linux/namespace.h +@@ -5,6 +5,9 @@ + #include + #include + ++/* exported for AppArmor (SubDomain) */ ++extern struct rw_semaphore namespace_sem; ++ + struct namespace { + atomic_t count; + struct vfsmount * root; diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/digest-apparmor-sources-2.6.17 b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/digest-apparmor-sources-2.6.17 new file mode 100644 index 0000000..0d2fbf9 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/digest-apparmor-sources-2.6.17 @@ -0,0 +1,9 @@ +MD5 cd5d67dc1d3514ec240497efff0f8726 genpatches-2.6.17-1.base.tar.bz2 3337 +RMD160 f9b5621fed8fcfee7da697d89097842287b41b24 genpatches-2.6.17-1.base.tar.bz2 3337 +SHA256 2208b72729dce6daef7dc5700192aec0ae17327c794681621d2123f0c483ae21 genpatches-2.6.17-1.base.tar.bz2 3337 +MD5 0b6385904bccbd9d6c5508565e5c76ff genpatches-2.6.17-1.extras.tar.bz2 138704 +RMD160 14b17e02b7893e6b023bee9e1b40f4bc85a30f05 genpatches-2.6.17-1.extras.tar.bz2 138704 +SHA256 fa10ec7d3d74c8bf57fb3bd01c33f83dcca3c1e4cd4601937cc2ef904cce1dfe genpatches-2.6.17-1.extras.tar.bz2 138704 +MD5 37ddefe96625502161f075b9d907f21e linux-2.6.17.tar.bz2 41272919 +RMD160 26aad30c9a6610665c6c7d62401d79bf56a6a699 linux-2.6.17.tar.bz2 41272919 +SHA256 ab0f647d52f124958439517df9e1ae0efda90cdb851f59f522fa1749f1d87d58 linux-2.6.17.tar.bz2 41272919 diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/digest-apparmor-sources-2.6.17-r1 b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/digest-apparmor-sources-2.6.17-r1 new file mode 100644 index 0000000..0d2fbf9 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/files/digest-apparmor-sources-2.6.17-r1 @@ -0,0 +1,9 @@ +MD5 cd5d67dc1d3514ec240497efff0f8726 genpatches-2.6.17-1.base.tar.bz2 3337 +RMD160 f9b5621fed8fcfee7da697d89097842287b41b24 genpatches-2.6.17-1.base.tar.bz2 3337 +SHA256 2208b72729dce6daef7dc5700192aec0ae17327c794681621d2123f0c483ae21 genpatches-2.6.17-1.base.tar.bz2 3337 +MD5 0b6385904bccbd9d6c5508565e5c76ff genpatches-2.6.17-1.extras.tar.bz2 138704 +RMD160 14b17e02b7893e6b023bee9e1b40f4bc85a30f05 genpatches-2.6.17-1.extras.tar.bz2 138704 +SHA256 fa10ec7d3d74c8bf57fb3bd01c33f83dcca3c1e4cd4601937cc2ef904cce1dfe genpatches-2.6.17-1.extras.tar.bz2 138704 +MD5 37ddefe96625502161f075b9d907f21e linux-2.6.17.tar.bz2 41272919 +RMD160 26aad30c9a6610665c6c7d62401d79bf56a6a699 linux-2.6.17.tar.bz2 41272919 +SHA256 ab0f647d52f124958439517df9e1ae0efda90cdb851f59f522fa1749f1d87d58 linux-2.6.17.tar.bz2 41272919 diff --git a/trunk/novell4gentoo/sys-kernel/apparmor-sources/metadata.xml b/trunk/novell4gentoo/sys-kernel/apparmor-sources/metadata.xml new file mode 100644 index 0000000..7e32869 --- /dev/null +++ b/trunk/novell4gentoo/sys-kernel/apparmor-sources/metadata.xml @@ -0,0 +1,5 @@ + + + +maintainer-wanted + diff --git a/trunk/novell4gentoo/sys-libs/libapparmor/ChangeLog b/trunk/novell4gentoo/sys-libs/libapparmor/ChangeLog new file mode 100644 index 0000000..183ffe0 --- /dev/null +++ b/trunk/novell4gentoo/sys-libs/libapparmor/ChangeLog @@ -0,0 +1,10 @@ +# ChangeLog for sys-libs/libapparmor +# Copyright 1999-2006 Gentoo Foundation; Distributed under the GPL v2 +# $Header: $ + + 13 Aug 2006; Mario Fetka + +libapparmor-2.0_p6288.ebuild: + Initial Import of + Matthew Snelham + Apparmor ebuilds + diff --git a/trunk/novell4gentoo/sys-libs/libapparmor/Manifest b/trunk/novell4gentoo/sys-libs/libapparmor/Manifest new file mode 100644 index 0000000..2b25b42 --- /dev/null +++ b/trunk/novell4gentoo/sys-libs/libapparmor/Manifest @@ -0,0 +1,8 @@ +DIST libapparmor-2.0-6288.tar.gz 20725 RMD160 5f4b8a443a23d4cd4a16b35ae8ebf65647683b2e SHA1 cd75d8631bdddb9e873cee5c6b3b45d12d94d416 SHA256 0aebaae48f1917a65c43683ad8f332301fcf46d4dcb392177a4750b245f42882 +EBUILD libapparmor-2.0_p6288.ebuild 833 RMD160 2cb68e615d17cb3a8d2dec04eb3e7591ff0b7b0c SHA1 a532c94dad06f14345b10056425eb7a83f86fede SHA256 62b95ca2859d69d52a6c61953323a436445de006e99f696bac2372652c805a62 +MD5 3211838fc17b65a3c9802623a6c7bf26 libapparmor-2.0_p6288.ebuild 833 +RMD160 2cb68e615d17cb3a8d2dec04eb3e7591ff0b7b0c libapparmor-2.0_p6288.ebuild 833 +SHA256 62b95ca2859d69d52a6c61953323a436445de006e99f696bac2372652c805a62 libapparmor-2.0_p6288.ebuild 833 +MD5 b2a797f50971ce7df339c695782874b2 files/digest-libapparmor-2.0_p6288 259 +RMD160 7088ab92b7dc5c741357ca3184eb79575a0832de files/digest-libapparmor-2.0_p6288 259 +SHA256 ae7d5dd76d792a1ff7ee7817846dff1402573c7fee7b19a45120086dac49e898 files/digest-libapparmor-2.0_p6288 259 diff --git a/trunk/novell4gentoo/sys-libs/libapparmor/files/digest-libapparmor-2.0_p6288 b/trunk/novell4gentoo/sys-libs/libapparmor/files/digest-libapparmor-2.0_p6288 new file mode 100644 index 0000000..74898c7 --- /dev/null +++ b/trunk/novell4gentoo/sys-libs/libapparmor/files/digest-libapparmor-2.0_p6288 @@ -0,0 +1,3 @@ +MD5 e7f5e6f8663919d5998469e842442509 libapparmor-2.0-6288.tar.gz 20725 +RMD160 5f4b8a443a23d4cd4a16b35ae8ebf65647683b2e libapparmor-2.0-6288.tar.gz 20725 +SHA256 0aebaae48f1917a65c43683ad8f332301fcf46d4dcb392177a4750b245f42882 libapparmor-2.0-6288.tar.gz 20725 diff --git a/trunk/novell4gentoo/sys-libs/libapparmor/libapparmor-2.0_p6288.ebuild b/trunk/novell4gentoo/sys-libs/libapparmor/libapparmor-2.0_p6288.ebuild new file mode 100644 index 0000000..379dd1b --- /dev/null +++ b/trunk/novell4gentoo/sys-libs/libapparmor/libapparmor-2.0_p6288.ebuild @@ -0,0 +1,38 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +inherit eutils toolchain-funcs + +MY_P="${P/_p/-}" +DESCRIPTION="Primary support library and headers for AppArmor userspace" +HOMEPAGE="http://forge.novell.com/modules/xfmod/project/?apparmor" +SRC_URI="http://forge.novell.com/modules/xfcontent/private.php/apparmor/Development%20-%20April%20Snapshot/${MY_P}.tar.gz" + +LICENSE="LGPL-2.1" +SLOT="0" +KEYWORDS="~x86" +IUSE="" + +DEPEND="virtual/libc" +RDEPEND="${DEPEND}" + +MY_S=${WORKDIR}/${P/_*/} + +src_unpack() { + unpack ${A} + + cd ${MY_S} + # the Make.rules isn't needed for Gentoo + sed -i "s/^include Make.rules//g" Makefile +} + +src_compile() { + cd ${MY_S} + emake CC="$(tc-getCC)" CFLAGS="${CFLAGS}" || die +} + +src_install() { + cd ${MY_S} + make DESTDIR="${D}" install || die +} diff --git a/trunk/novell4gentoo/sys-libs/libapparmor/metadata.xml b/trunk/novell4gentoo/sys-libs/libapparmor/metadata.xml new file mode 100644 index 0000000..7e32869 --- /dev/null +++ b/trunk/novell4gentoo/sys-libs/libapparmor/metadata.xml @@ -0,0 +1,5 @@ + + + +maintainer-wanted +