Add ignored *.diff files of the xen patches
authorArne Fitzenreiter <arne_f@ipfire.org>
Thu, 19 Nov 2009 20:15:28 +0000 (21:15 +0100)
committerArne Fitzenreiter <arne_f@ipfire.org>
Thu, 19 Nov 2009 20:15:28 +0000 (21:15 +0100)
188 files changed:
src/patches/suse-2.6.27.39/patches.apparmor/add-path_permission.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-2.6.25.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-audit.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-intree.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-lsm.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-main.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-misc.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-module_interface.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-network.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-ptrace-2.6.27.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/apparmor-rlimits.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/d_namespace_path.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/d_namespace_path_oops_fix.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/do_path_lookup-nameidata.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/file-handle-ops.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fix-complain.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fix-deleted-leak.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fix-security-param.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fix-vfs_rmdir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fork-tracking.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fsetattr-reintro-ATTR_FILE.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fsetattr-restore-ia_file.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/fsetattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/remove_suid.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-create.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-getxattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-link.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-listxattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-mkdir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-mknod.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-readlink.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-removexattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-rename.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-rmdir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-setattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-setxattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-symlink.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-unlink.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/security-xattr-file.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/sysctl-pathname.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/unambiguous-__d_path.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-getxattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-link.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-listxattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-mkdir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-mknod.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-notify_change.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-removexattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-rename.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-rmdir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-setxattr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-symlink.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.apparmor/vfs-unlink.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/ia64-node_mem_map-node_start_pfn.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/ppc-axon-missing-msi-workaround-5.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/ppc-dynamic-reconfiguration.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/ppc-vmcoreinfo.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-04-06-cio-sac-update.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-07-01-zfcp-port-failed-message.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-07-02-zfcp-unchained-fsf.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-07-03-topology-fix.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-08-01-cio-fix-mp-mode.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-08-02-zfcp-gpn-align-fix.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-08-03-iucv-cpu-hotremove.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-08-04-compat-sigaltstack.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-09-04-topology.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/s390-15-09-idle-accounting.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.arch/x86_64_make_calibrate_APIC_clock_SMI-safe.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-change-fcoe_sw-sg_tablesi.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-check-return-for-fc_set_m.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-fix-frame-length-validati.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-fix-incorrect-use-of-struct-module.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-improved-load-balancing-i.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-logoff-of-the-fabric-when.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-remove-warn_on-in-fc_set.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/fcoe-user_mfs-is-never-used.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-add-fc_disc-c-locking-co.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-check-for-err-when-recv-state-is-incorrect.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-ensure-correct-device_pu.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-handle-rrq-exch-timeout.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-improve-fc_lport-c-locki.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-improve-fc_rport-c-locki.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-make-fc_disc-inline-with.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-make-rscn-parsing-more-r.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-make-sure-we-access-the.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-pass-lport-in-exch_mgr_r.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-set-the-release-function.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-updated-comment-for-orde.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-updated-libfc-fcoe-modul.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-use-an-operations-struct.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc-when-rport-goes-away-re.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc_locking.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.drivers/libfc_rport.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/b43legacy-fix-led_device_naming.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/bug-531716_ocfs2-SLE11-dentry_lock_drop_flush.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/ext2_mtime_update_on_rename.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/ext3_false_EIO_fix.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/ia64-sparse-fixes.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/ia64_uv_partition_id.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/ia64_uv_watchlist.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/iwlwifi-fix-iwl-3945_led_device_naming.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/iwlwifi-fix-iwl-led_device_naming.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/kdb-kdump.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/kdb-oops-panic.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/kdb-read-CR.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/kdump-x86-sparsemem.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/make-note_interrupt-fast.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/nfs-acl-caching.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/nfsd4-posix-acl-to-nfs4-acl-conversion-isues.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/proc-scsi-scsi-fix.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/rt2x00-fix-led_device_naming.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/sd_liberal_28_sense_invalid.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-bios_call_memprotect.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-bios_call_partition.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-bios_call_reserve_page.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-bios_call_watchlist.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-bios_common.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-efi_bios.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-sn_region_size.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-sysfs.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-xp-change_memprotect.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-xpc-get_sn_info.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-xpc_create_gru_mq_uv.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv-xpc_get_part_rsvd_page.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.fixes/uv_setup_irq.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.kernel.org/ipmi-section-conflict.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.kernel.org/psmouse-section-conflict.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.rpmify/cloneconfig.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/apm_setup_UP.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/crasher-26.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/file-capabilities-add-file_caps-switch.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/file-capabilities-disable-by-default.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/fs-knows-MAY_APPEND.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/fs-may_iops.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/genksyms-add-override-flag.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/genksyms-override.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/genksyms-reference.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/kdb-resolve-uv-conflict.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/nfs4acl-ai.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/nfs4acl-common.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/nfs4acl-ext3.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/nfsacl-client-cache-CHECK.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/novfs-map-drives-correctly.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/novfs-merge-changes.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/panic-on-io-nmi.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/parser-match_string.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/raw_device_max_minors_param.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-add-reiserfs_error.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-buffer-info-for-balance.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-cleanup-path-funcs.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-consistent-messages.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-eliminate-per-super-xattr-lock.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-journaled-xattrs.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-kill-xattr-readdir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-make-per-inode-xattr-locking-more-fine-grained.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rearrange-journal-abort.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-reiserfs-warning.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-reiserfs_info.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-reiserfs_panic.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-remove-i_has_xattr_dir.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-remove-link-detection.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rename-._.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rename-p_._.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rename-p_s_bh.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rename-p_s_inode.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rename-p_s_sb.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-rename-p_s_tb.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-selinux.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-simplify-buffer-info.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-simplify-xattr-internal-file-lookups-opens.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-strip-whitespace.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-use-generic-xattr-handlers.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/reiserfs-use-reiserfs_error.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/s390-Kerntypes.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/s390-System.map.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.suse/usb_correct_config_ti_04b3_4543.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.trace/ftrace-framepointer.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.trace/s390-syscall-get-nr.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-arch-i386.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-arch-x86.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-arch-x86_64.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-common.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-include-xen-interface.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-xen-arch.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-xen-drivers.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-auto-xen-kconfig.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-panic-on-io-nmi.diff [new file with mode: 0644]
src/patches/suse-2.6.27.39/patches.xen/xen3-uv_setup_irq.diff [new file with mode: 0644]

diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/add-path_permission.diff b/src/patches/suse-2.6.27.39/patches.apparmor/add-path_permission.diff
new file mode 100644 (file)
index 0000000..0eb796d
--- /dev/null
@@ -0,0 +1,201 @@
+From: Jeff Mahoney <jeffm@suse.com>
+Subject: [PATCH] vfs: introduce path_permission()
+
+ 2.6.27 eliminated the nameidata parameter from permission and replaced
+ several call sites with inode_permission. This keeps the information
+ required by AppArmor from reaching it.
+
+ The following patch factors out the permission assessment part of
+ inode_permission into __inode_permission and adds a path_permission
+ function that takes a struct path instead of a struct inode and passes
+ it to security_path_permission instead of security_inode_permission.
+
+ All of the call sites that had access to a struct path whether by
+ itself or via a file or nameidata (and used it) in 2.6.26 are changed
+ to use the path_permission call.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+---
+ fs/inotify_user.c  |    2 +-
+ fs/namei.c         |   32 ++++++++++++++++++++++++--------
+ fs/open.c          |   10 +++++-----
+ include/linux/fs.h |    5 +++++
+ 4 files changed, 35 insertions(+), 14 deletions(-)
+
+--- a/fs/inotify_user.c
++++ b/fs/inotify_user.c
+@@ -372,7 +372,7 @@ static int find_inode(const char __user
+       if (error)
+               return error;
+       /* you can only watch an inode if you have read permissions on it */
+-      error = inode_permission(path->dentry->d_inode, MAY_READ);
++      error = path_permission(path, MAY_READ);
+       if (error)
+               path_put(path);
+       return error;
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -227,7 +227,7 @@ int generic_permission(struct inode *ino
+       return -EACCES;
+ }
+-int inode_permission(struct inode *inode, int mask)
++static int __inode_permission(struct inode *inode, int mask)
+ {
+       int retval;
+       int submask = mask;
+@@ -273,7 +273,12 @@ int inode_permission(struct inode *inode
+       if (retval)
+               return retval;
+-      retval = devcgroup_inode_permission(inode, mask);
++      return devcgroup_inode_permission(inode, mask);
++}
++
++int inode_permission(struct inode *inode, int mask)
++{
++      int retval = __inode_permission(inode, mask);
+       if (retval)
+               return retval;
+@@ -281,6 +286,15 @@ int inode_permission(struct inode *inode
+                       mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
+ }
++int path_permission(struct path *path, int mask)
++{
++      int retval = __inode_permission(path->dentry->d_inode, mask);
++      if (retval)
++              return retval;
++      return security_path_permission(path,
++                      mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
++}
++
+ /**
+  * vfs_permission  -  check for access rights to a given path
+  * @nd:               lookup result that describes the path
+@@ -293,7 +307,7 @@ int inode_permission(struct inode *inode
+  */
+ int vfs_permission(struct nameidata *nd, int mask)
+ {
+-      return inode_permission(nd->path.dentry->d_inode, mask);
++      return path_permission(&nd->path, mask);
+ }
+ /**
+@@ -310,7 +324,7 @@ int vfs_permission(struct nameidata *nd,
+  */
+ int file_permission(struct file *file, int mask)
+ {
+-      return inode_permission(file->f_path.dentry->d_inode, mask);
++      return path_permission(&file->f_path, mask);
+ }
+ /*
+@@ -452,8 +466,9 @@ static struct dentry * cached_lookup(str
+  * short-cut DAC fails, then call permission() to do more
+  * complete permission check.
+  */
+-static int exec_permission_lite(struct inode *inode)
++static int exec_permission_lite(struct path *path)
+ {
++      struct inode *inode = path->dentry->d_inode;
+       umode_t mode = inode->i_mode;
+       if (inode->i_op && inode->i_op->permission)
+@@ -478,7 +493,7 @@ static int exec_permission_lite(struct i
+       return -EACCES;
+ ok:
+-      return security_inode_permission(inode, MAY_EXEC);
++      return security_path_permission(path, MAY_EXEC);
+ }
+ /*
+@@ -875,7 +890,7 @@ static int __link_path_walk(const char *
+               unsigned int c;
+               nd->flags |= LOOKUP_CONTINUE;
+-              err = exec_permission_lite(inode);
++              err = exec_permission_lite(&nd->path);
+               if (err == -EAGAIN)
+                       err = vfs_permission(nd, MAY_EXEC);
+               if (err)
+@@ -1250,7 +1265,7 @@ static struct dentry *lookup_hash(struct
+ {
+       int err;
+-      err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
++      err = path_permission(&nd->path, MAY_EXEC);
+       if (err)
+               return ERR_PTR(err);
+       return __lookup_hash(&nd->last, nd->path.dentry, nd);
+@@ -2907,6 +2922,7 @@ EXPORT_SYMBOL(page_symlink_inode_operati
+ EXPORT_SYMBOL(path_lookup);
+ EXPORT_SYMBOL(vfs_path_lookup);
+ EXPORT_SYMBOL(inode_permission);
++EXPORT_SYMBOL(path_permission);
+ EXPORT_SYMBOL(vfs_permission);
+ EXPORT_SYMBOL(file_permission);
+ EXPORT_SYMBOL(unlock_rename);
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -248,7 +248,7 @@ static long do_sys_truncate(const char _
+       if (error)
+               goto dput_and_out;
+-      error = inode_permission(inode, MAY_WRITE);
++      error = path_permission(&path, MAY_WRITE);
+       if (error)
+               goto mnt_drop_write_and_out;
+@@ -493,7 +493,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, con
+                       goto out_path_release;
+       }
+-      res = inode_permission(inode, mode | MAY_ACCESS);
++      res = path_permission(&path, mode | MAY_ACCESS);
+       /* SuS v2 requires we report a read only fs too */
+       if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
+               goto out_path_release;
+@@ -536,7 +536,7 @@ SYSCALL_DEFINE1(chdir, const char __user
+       if (error)
+               goto out;
+-      error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
++      error = path_permission(&path, MAY_EXEC | MAY_ACCESS);
+       if (error)
+               goto dput_and_out;
+@@ -565,7 +565,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd
+       if (!S_ISDIR(inode->i_mode))
+               goto out_putf;
+-      error = inode_permission(inode, MAY_EXEC | MAY_ACCESS);
++      error = path_permission(&file->f_path, MAY_EXEC | MAY_ACCESS);
+       if (!error)
+               set_fs_pwd(current->fs, &file->f_path);
+ out_putf:
+@@ -583,7 +583,7 @@ SYSCALL_DEFINE1(chroot, const char __use
+       if (error)
+               goto out;
+-      error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
++      error = path_permission(&path, MAY_EXEC | MAY_ACCESS);
+       if (error)
+               goto dput_and_out;
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -1201,6 +1201,11 @@ extern void dentry_unhash(struct dentry
+ extern int file_permission(struct file *, int);
+ /*
++ * VFS path helper functions.
++ */
++extern int path_permission(struct path *, int);
++
++/*
+  * File types
+  *
+  * NOTE! These match bits 12..15 of stat.st_mode
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-2.6.25.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-2.6.25.diff
new file mode 100644 (file)
index 0000000..56bf22a
--- /dev/null
@@ -0,0 +1,47 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: Patch AppArmor for 2.6.25 kernel
+
+Add 64 bit capabilities support to AppArmor.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ security/apparmor/module_interface.c |   22 ++++++++++++++++++----
+ 1 file changed, 18 insertions(+), 4 deletions(-)
+
+--- a/security/apparmor/module_interface.c
++++ b/security/apparmor/module_interface.c
+@@ -395,15 +395,29 @@ static struct aa_profile *aa_unpack_prof
+       if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+               goto fail;
+-      if (!aa_is_u32(e, &(profile->capabilities), NULL))
++      if (!aa_is_u32(e, &(profile->capabilities.cap[0]), NULL))
+               goto fail;
+-      if (!aa_is_u32(e, &(profile->audit_caps), NULL))
++      if (!aa_is_u32(e, &(profile->audit_caps.cap[0]), NULL))
+               goto fail;
+-      if (!aa_is_u32(e, &(profile->quiet_caps), NULL))
++      if (!aa_is_u32(e, &(profile->quiet_caps.cap[0]), NULL))
+               goto fail;
+-      if (!aa_is_u32(e, &(profile->set_caps), NULL))
++      if (!aa_is_u32(e, &(profile->set_caps.cap[0]), NULL))
+               goto fail;
++      if (aa_is_nameX(e, AA_STRUCT, "caps64")) {
++              /* optional upper half of 64 bit caps */
++              if (!aa_is_u32(e, &(profile->capabilities.cap[1]), NULL))
++                      goto fail;
++              if (!aa_is_u32(e, &(profile->audit_caps.cap[1]), NULL))
++                      goto fail;
++              if (!aa_is_u32(e, &(profile->quiet_caps.cap[1]), NULL))
++                      goto fail;
++              if (!aa_is_u32(e, &(profile->set_caps.cap[1]), NULL))
++                      goto fail;
++              if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
++                      goto fail;
++      }
++
+       if (!aa_unpack_rlimits(e, profile))
+               goto fail;
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-audit.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-audit.diff
new file mode 100644 (file)
index 0000000..37ee484
--- /dev/null
@@ -0,0 +1,72 @@
+From: Tony Jones <tonyj@suse.de>
+Subject: Export audit subsystem for use by modules
+
+Update kenel audit range comments to show AppArmor's registered range of
+1500-1599.  This range used to be reserved for LSPP but LSPP uses the
+SE Linux range and the range was given to AppArmor.
+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
+
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ include/linux/audit.h |   12 +++++++++++-
+ kernel/audit.c        |    6 ++++--
+ 2 files changed, 15 insertions(+), 3 deletions(-)
+
+--- a/include/linux/audit.h
++++ b/include/linux/audit.h
+@@ -33,7 +33,7 @@
+  * 1200 - 1299 messages internal to the audit daemon
+  * 1300 - 1399 audit event messages
+  * 1400 - 1499 SE Linux use
+- * 1500 - 1599 kernel LSPP events
++ * 1500 - 1599 AppArmor use
+  * 1600 - 1699 kernel crypto events
+  * 1700 - 1799 kernel anomaly records
+  * 1800 - 1999 future kernel use (maybe integrity labels and related events)
+@@ -119,6 +119,13 @@
+ #define AUDIT_MAC_UNLBL_STCADD        1416    /* NetLabel: add a static label */
+ #define AUDIT_MAC_UNLBL_STCDEL        1417    /* NetLabel: del a static label */
++#define AUDIT_APPARMOR_AUDIT  1501    /* AppArmor audited grants */
++#define AUDIT_APPARMOR_ALLOWED        1502    /* Allowed Access for learning */
++#define AUDIT_APPARMOR_DENIED 1503
++#define AUDIT_APPARMOR_HINT   1504    /* Process Tracking information */
++#define AUDIT_APPARMOR_STATUS 1505    /* Changes in config */
++#define AUDIT_APPARMOR_ERROR  1506    /* Internal AppArmor Errors */
++
+ #define AUDIT_FIRST_KERN_ANOM_MSG   1700
+ #define AUDIT_LAST_KERN_ANOM_MSG    1799
+ #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
+@@ -545,6 +552,9 @@ extern void                    audit_log(struct audit_
+                                     __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)));
+--- a/kernel/audit.c
++++ b/kernel/audit.c
+@@ -1231,8 +1231,7 @@ static inline int audit_expand(struct au
+  * 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,
+-                            va_list args)
++void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
+ {
+       int len, avail;
+       struct sk_buff *skb;
+@@ -1506,3 +1505,6 @@ EXPORT_SYMBOL(audit_log_start);
+ EXPORT_SYMBOL(audit_log_end);
+ EXPORT_SYMBOL(audit_log_format);
+ EXPORT_SYMBOL(audit_log);
++EXPORT_SYMBOL_GPL(audit_log_vformat);
++EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
++EXPORT_SYMBOL_GPL(audit_log_d_path);
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-intree.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-intree.diff
new file mode 100644 (file)
index 0000000..7c29501
--- /dev/null
@@ -0,0 +1,31 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: Add AppArmor LSM to security/Makefile
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+
+---
+ security/Kconfig  |    1 +
+ security/Makefile |    3 ++-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/security/Kconfig
++++ b/security/Kconfig
+@@ -97,6 +97,7 @@ config SECURITY_ROOTPLUG
+ source security/selinux/Kconfig
+ source security/smack/Kconfig
++source security/apparmor/Kconfig
+ endmenu
+--- a/security/Makefile
++++ b/security/Makefile
+@@ -14,5 +14,6 @@ obj-$(CONFIG_SECURITY)                       += security.o c
+ # Must precede capability.o in order to stack properly.
+ obj-$(CONFIG_SECURITY_SELINUX)                += selinux/built-in.o
+ obj-$(CONFIG_SECURITY_SMACK)          += smack/built-in.o
+-obj-$(CONFIG_SECURITY_ROOTPLUG)               += root_plug.o
++obj-$(CONFIG_SECURITY_APPARMOR)         += commoncap.o apparmor/
++ obj-$(CONFIG_SECURITY_ROOTPLUG)              += root_plug.o
+ obj-$(CONFIG_CGROUP_DEVICE)           += device_cgroup.o
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-lsm.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-lsm.diff
new file mode 100644 (file)
index 0000000..b211994
--- /dev/null
@@ -0,0 +1,910 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: Module and LSM hooks
+
+Module parameters, LSM hooks, initialization and teardown.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+
+---
+ security/apparmor/lsm.c |  895 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 895 insertions(+)
+
+--- /dev/null
++++ b/security/apparmor/lsm.c
+@@ -0,0 +1,895 @@
++/*
++ *    Copyright (C) 1998-2007 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 LSM interface
++ */
++
++#include <linux/security.h>
++#include <linux/module.h>
++#include <linux/mm.h>
++#include <linux/mman.h>
++#include <linux/mount.h>
++#include <linux/namei.h>
++#include <linux/ctype.h>
++#include <linux/sysctl.h>
++#include <linux/audit.h>
++
++#include "apparmor.h"
++#include "inline.h"
++
++/* Flag indicating whether initialization completed */
++int apparmor_initialized = 0;
++
++static int param_set_aabool(const char *val, struct kernel_param *kp);
++static int param_get_aabool(char *buffer, struct kernel_param *kp);
++#define param_check_aabool(name, p) __param_check(name, p, int)
++
++static int param_set_aauint(const char *val, struct kernel_param *kp);
++static int param_get_aauint(char *buffer, struct kernel_param *kp);
++#define param_check_aauint(name, p) __param_check(name, p, int)
++
++/* Flag values, also controllable via /sys/module/apparmor/parameters
++ * We define special types as we want to do additional mediation.
++ *
++ * 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, aabool, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode");
++
++/* Debug mode */
++int apparmor_debug = 0;
++module_param_named(debug, apparmor_debug, aabool, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode");
++
++/* Audit mode */
++int apparmor_audit = 0;
++module_param_named(audit, apparmor_audit, aabool, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode");
++
++/* Syscall logging mode */
++int apparmor_logsyscall = 0;
++module_param_named(logsyscall, apparmor_logsyscall, aabool, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode");
++
++/* Maximum pathname length before accesses will start getting rejected */
++unsigned int apparmor_path_max = 2 * PATH_MAX;
++module_param_named(path_max, apparmor_path_max, aauint, S_IRUSR | S_IWUSR);
++MODULE_PARM_DESC(apparmor_path_max, "Maximum pathname length allowed");
++
++/* Boot time disable flag */
++#ifdef CONFIG_SECURITY_APPARMOR_DISABLE
++#define AA_ENABLED_PERMS 0600
++#else
++#define AA_ENABLED_PERMS 0400
++#endif
++static int param_set_aa_enabled(const char *val, struct kernel_param *kp);
++unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
++module_param_call(enabled, param_set_aa_enabled, param_get_aauint,
++                &apparmor_enabled, AA_ENABLED_PERMS);
++MODULE_PARM_DESC(apparmor_enabled, "Enable/Disable Apparmor on boot");
++
++static int __init apparmor_enabled_setup(char *str)
++{
++      apparmor_enabled = simple_strtol(str, NULL, 0);
++      return 1;
++}
++__setup("apparmor=", apparmor_enabled_setup);
++
++static int param_set_aabool(const char *val, struct kernel_param *kp)
++{
++      if (aa_task_context(current))
++              return -EPERM;
++      return param_set_bool(val, kp);
++}
++
++static int param_get_aabool(char *buffer, struct kernel_param *kp)
++{
++      if (aa_task_context(current))
++              return -EPERM;
++      return param_get_bool(buffer, kp);
++}
++
++static int param_set_aauint(const char *val, struct kernel_param *kp)
++{
++      if (aa_task_context(current))
++              return -EPERM;
++      return param_set_uint(val, kp);
++}
++
++static int param_get_aauint(char *buffer, struct kernel_param *kp)
++{
++      if (aa_task_context(current))
++              return -EPERM;
++      return param_get_uint(buffer, kp);
++}
++
++/* allow run time disabling of apparmor */
++static int param_set_aa_enabled(const char *val, struct kernel_param *kp)
++{
++      char *endp;
++      unsigned long l;
++
++      if (!apparmor_initialized) {
++              apparmor_enabled = 0;
++              return 0;
++      }
++
++      if (aa_task_context(current))
++              return -EPERM;
++
++      if (!apparmor_enabled)
++              return -EINVAL;
++
++      if (!val)
++              return -EINVAL;
++
++      l = simple_strtoul(val, &endp, 0);
++      if (endp == val || l != 0)
++              return -EINVAL;
++
++      apparmor_enabled = 0;
++      apparmor_disable();
++      return 0;
++}
++
++static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
++                           const char *name)
++{
++      struct aa_profile *profile = aa_get_profile(task);
++      int error = 0;
++
++      if (profile) {
++              error = aa_audit_syscallreject(profile, flags, name);
++              aa_put_profile(profile);
++      }
++
++      return error;
++}
++
++static int apparmor_ptrace(struct task_struct *parent,
++                         struct task_struct *child, unsigned int mode)
++{
++      struct aa_task_context *cxt;
++      int error = 0;
++
++      /*
++       * parent can ptrace child when
++       * - parent is unconfined
++       * - parent & child are in the same namespace &&
++       *   - parent is in complain mode
++       *   - parent and child are confined by the same profile
++       *   - parent profile has CAP_SYS_PTRACE
++       */
++
++      rcu_read_lock();
++      cxt = aa_task_context(parent);
++      if (cxt) {
++              if (parent->nsproxy != child->nsproxy) {
++                      struct aa_audit sa;
++                      memset(&sa, 0, sizeof(sa));
++                      sa.operation = "ptrace";
++                      sa.gfp_mask = GFP_ATOMIC;
++                      sa.parent = parent->pid;
++                      sa.task = child->pid;
++                      sa.info = "different namespaces";
++                      aa_audit_reject(cxt->profile, &sa);
++                      error = -EPERM;
++              } else {
++                      struct aa_task_context *child_cxt =
++                              aa_task_context(child);
++
++                      error = aa_may_ptrace(cxt, child_cxt ?
++                                                 child_cxt->profile : NULL);
++                      if (PROFILE_COMPLAIN(cxt->profile)) {
++                              struct aa_audit sa;
++                              memset(&sa, 0, sizeof(sa));
++                              sa.operation = "ptrace";
++                              sa.gfp_mask = GFP_ATOMIC;
++                              sa.parent = parent->pid;
++                              sa.task = child->pid;
++                              aa_audit_hint(cxt->profile, &sa);
++                      }
++              }
++      }
++      rcu_read_unlock();
++
++      return error;
++}
++
++static int apparmor_capable(struct task_struct *task, int cap)
++{
++      int error;
++      struct aa_task_context *cxt;
++
++      /* cap_capable returns 0 on success, else -EPERM */
++      error = cap_capable(task, cap);
++
++      rcu_read_lock();
++      cxt = aa_task_context(task);
++      if (cxt && (!error || cap_raised(cxt->profile->set_caps, cap)))
++              error = aa_capability(cxt, cap);
++      rcu_read_unlock();
++
++      return error;
++}
++
++static int apparmor_sysctl(struct ctl_table *table, int op)
++{
++      struct aa_profile *profile = aa_get_profile(current);
++      int error = 0;
++
++      if (profile) {
++              char *buffer, *name;
++              int mask;
++
++              mask = 0;
++              if (op & 4)
++                      mask |= MAY_READ;
++              if (op & 2)
++                      mask |= MAY_WRITE;
++
++              error = -ENOMEM;
++              buffer = (char*)__get_free_page(GFP_KERNEL);
++              if (!buffer)
++                      goto out;
++              name = sysctl_pathname(table, buffer, PAGE_SIZE);
++              if (name && name - buffer >= 5) {
++                      name -= 5;
++                      memcpy(name, "/proc", 5);
++                      error = aa_perm_path(profile, "sysctl", name, mask, 0);
++              }
++              free_page((unsigned long)buffer);
++      }
++
++out:
++      aa_put_profile(profile);
++      return error;
++}
++
++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);
++}
++
++static int apparmor_bprm_secureexec(struct linux_binprm *bprm)
++{
++      int ret = cap_bprm_secureexec(bprm);
++
++      if (!ret && (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) {
++              AA_DEBUG("%s: secureexec required for %s\n",
++                       __FUNCTION__, bprm->filename);
++              ret = 1;
++      }
++
++      return ret;
++}
++
++static int apparmor_sb_mount(char *dev_name, struct path *path, char *type,
++                            unsigned long flags, void *data)
++{
++      return aa_reject_syscall(current, GFP_KERNEL, "mount");
++}
++
++static int apparmor_umount(struct vfsmount *mnt, int flags)
++{
++      return aa_reject_syscall(current, GFP_KERNEL, "umount");
++}
++
++static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
++                              struct vfsmount *mnt, int mask)
++{
++      struct aa_profile *profile;
++      int error = 0;
++
++      if (!mnt || !mediated_filesystem(dir))
++              goto out;
++
++      profile = aa_get_profile(current);
++
++      if (profile)
++              error = aa_perm_dir(profile, "inode_mkdir", dentry, mnt,
++                                  MAY_WRITE);
++
++      aa_put_profile(profile);
++
++out:
++      return error;
++}
++
++static int apparmor_inode_rmdir(struct inode *dir, struct dentry *dentry,
++                              struct vfsmount *mnt)
++{
++      struct aa_profile *profile;
++      int error = 0;
++
++      if (!mnt || !mediated_filesystem(dir))
++              goto out;
++
++      profile = aa_get_profile(current);
++
++      if (profile)
++              error = aa_perm_dir(profile, "inode_rmdir", dentry, mnt,
++                                  MAY_WRITE);
++
++      aa_put_profile(profile);
++
++out:
++      return error;
++}
++
++static int aa_permission(const char *operation, struct inode *inode,
++                       struct dentry *dentry, struct vfsmount *mnt,
++                       int mask, int check)
++{
++      int error = 0;
++
++      if (mnt && mediated_filesystem(inode)) {
++              struct aa_profile *profile;
++
++              profile = aa_get_profile(current);
++              if (profile)
++                      error = aa_perm(profile, operation, dentry, mnt, mask,
++                                      check);
++              aa_put_profile(profile);
++      }
++      return error;
++}
++
++static inline int aa_mask_permissions(int mask)
++{
++      if (mask & MAY_APPEND)
++              mask &= (MAY_READ | MAY_APPEND | MAY_EXEC);
++      else
++              mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
++      return mask;
++}
++
++static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
++                               struct vfsmount *mnt, int mask)
++{
++      return aa_permission("inode_create", dir, dentry, mnt, MAY_APPEND, 0);
++}
++
++static int apparmor_inode_link(struct dentry *old_dentry,
++                             struct vfsmount *old_mnt, struct inode *dir,
++                             struct dentry *new_dentry,
++                             struct vfsmount *new_mnt)
++{
++      int error = 0;
++      struct aa_profile *profile;
++
++      if (!old_mnt || !new_mnt || !mediated_filesystem(dir))
++              goto out;
++
++      profile = aa_get_profile(current);
++
++      if (profile)
++              error = aa_link(profile, new_dentry, new_mnt,
++                              old_dentry, old_mnt);
++
++      aa_put_profile(profile);
++
++out:
++      return error;
++}
++
++static int apparmor_inode_unlink(struct inode *dir, struct dentry *dentry,
++                               struct vfsmount *mnt)
++{
++      int check = 0;
++
++      if (S_ISDIR(dentry->d_inode->i_mode))
++              check |= AA_CHECK_DIR;
++      return aa_permission("inode_unlink", dir, dentry, mnt, MAY_WRITE,
++                           check);
++}
++
++static int apparmor_inode_symlink(struct inode *dir, struct dentry *dentry,
++                                struct vfsmount *mnt, const char *old_name)
++{
++      return aa_permission("inode_symlink", dir, dentry, mnt, MAY_WRITE, 0);
++}
++
++static int apparmor_inode_mknod(struct inode *dir, struct dentry *dentry,
++                              struct vfsmount *mnt, int mode, dev_t dev)
++{
++      return aa_permission("inode_mknod", dir, dentry, mnt, MAY_WRITE, 0);
++}
++
++static int apparmor_inode_rename(struct inode *old_dir,
++                               struct dentry *old_dentry,
++                               struct vfsmount *old_mnt,
++                               struct inode *new_dir,
++                               struct dentry *new_dentry,
++                               struct vfsmount *new_mnt)
++{
++      struct aa_profile *profile;
++      int error = 0;
++
++      if ((!old_mnt && !new_mnt) || !mediated_filesystem(old_dir))
++              goto out;
++
++      profile = aa_get_profile(current);
++
++      if (profile) {
++              struct inode *inode = old_dentry->d_inode;
++              int check = 0;
++
++              if (inode && S_ISDIR(inode->i_mode))
++                      check |= AA_CHECK_DIR;
++              if (old_mnt)
++                      error = aa_perm(profile, "inode_rename", old_dentry,
++                                      old_mnt, MAY_READ | MAY_WRITE, check);
++
++              if (!error && new_mnt) {
++                      error = aa_perm(profile, "inode_rename", new_dentry,
++                                      new_mnt, MAY_WRITE, check);
++              }
++      }
++
++      aa_put_profile(profile);
++
++out:
++      return error;
++}
++
++static int apparmor_inode_permission(struct inode *inode, int mask,
++                                   struct nameidata *nd)
++{
++      int check = 0;
++
++      if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE))
++              return 0;
++      mask = aa_mask_permissions(mask);
++      if (S_ISDIR(inode->i_mode)) {
++              check |= AA_CHECK_DIR;
++              /* allow traverse accesses to directories */
++              mask &= ~MAY_EXEC;
++      }
++      return aa_permission("inode_permission", inode, nd->dentry, nd->mnt,
++                           mask, check);
++}
++
++static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
++                                struct iattr *iattr)
++{
++      int error = 0;
++
++      if (!mnt)
++              goto out;
++
++      if (mediated_filesystem(dentry->d_inode)) {
++              struct aa_profile *profile;
++
++              profile = aa_get_profile(current);
++              /*
++               * Mediate any attempt to change attributes of a file
++               * (chmod, chown, chgrp, etc)
++               */
++              if (profile)
++                      error = aa_attr(profile, dentry, mnt, iattr);
++
++              aa_put_profile(profile);
++      }
++
++out:
++      return error;
++}
++
++static int aa_xattr_permission(struct dentry *dentry, struct vfsmount *mnt,
++                             const char *operation, int mask,
++                             struct file *file)
++{
++      int error = 0;
++
++      if (mnt && mediated_filesystem(dentry->d_inode)) {
++              struct aa_profile *profile = aa_get_profile(current);
++              int check = file ? AA_CHECK_FD : 0;
++
++              if (profile)
++                      error = aa_perm_xattr(profile, operation, dentry, mnt,
++                                            mask, check);
++              aa_put_profile(profile);
++      }
++
++      return error;
++}
++
++static int apparmor_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
++                                 const char *name, const void *value,
++                                 size_t size, int flags, struct file *file)
++{
++      int error = cap_inode_setxattr(dentry, mnt, name, value, size, flags,
++                                     file);
++
++      if (!error)
++              error = aa_xattr_permission(dentry, mnt, "xattr set",
++                                          MAY_WRITE, file);
++      return error;
++}
++
++static int apparmor_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
++                                 const char *name, struct file *file)
++{
++      return aa_xattr_permission(dentry, mnt, "xattr get", MAY_READ, file);
++}
++
++static int apparmor_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
++                                  struct file *file)
++{
++      return aa_xattr_permission(dentry, mnt, "xattr list", MAY_READ, file);
++}
++
++static int apparmor_inode_removexattr(struct dentry *dentry,
++                                    struct vfsmount *mnt, const char *name,
++                                    struct file *file)
++{
++      return aa_xattr_permission(dentry, mnt, "xattr remove", MAY_WRITE,
++                                 file);
++}
++
++static int aa_file_permission(const char *op, struct file *file, int mask)
++{
++      struct aa_profile *profile;
++      struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
++      int error = 0;
++
++      if (!file_profile)
++              goto out;
++
++      /*
++       * If this file was opened under a different profile, we
++       * revalidate the access against the current profile.
++       */
++      profile = aa_get_profile(current);
++      if (profile && (file_profile != profile || mask & AA_MAY_LOCK)) {
++              struct dentry *dentry = file->f_dentry;
++              struct vfsmount *mnt = file->f_vfsmnt;
++              struct inode *inode = dentry->d_inode;
++              int check = AA_CHECK_FD;
++
++              /*
++               * FIXME: We should remember which profiles we revalidated
++               *        against.
++               */
++              if (S_ISDIR(inode->i_mode))
++                      check |= AA_CHECK_DIR;
++              error = aa_permission(op, inode, dentry, mnt, mask, check);
++      }
++      aa_put_profile(profile);
++
++out:
++      return error;
++}
++
++static int apparmor_file_permission(struct file *file, int mask)
++{
++      return aa_file_permission("file_permission", file,
++                                aa_mask_permissions(mask));
++}
++
++static inline int apparmor_file_lock (struct file *file, unsigned int cmd)
++{
++      int mask = AA_MAY_LOCK;
++      if (cmd == F_WRLCK)
++              mask |= MAY_WRITE;
++      return aa_file_permission("file_lock", file, mask);
++}
++
++static int apparmor_file_alloc_security(struct file *file)
++{
++      struct aa_profile *profile;
++
++      profile = aa_get_profile(current);
++      if (profile)
++              file->f_security = profile;
++
++      return 0;
++}
++
++static void apparmor_file_free_security(struct file *file)
++{
++      struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
++
++      aa_put_profile(file_profile);
++}
++
++static inline int aa_mmap(struct file *file, const char *operation,
++                        unsigned long prot, unsigned long flags)
++{
++      struct dentry *dentry;
++      int mask = 0;
++
++      if (!file || !file->f_security)
++              return 0;
++
++      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 |= AA_EXEC_MMAP;
++
++      dentry = file->f_dentry;
++      return aa_permission(operation, dentry->d_inode, dentry,
++                           file->f_vfsmnt, mask, AA_CHECK_FD);
++}
++
++static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
++                            unsigned long prot, unsigned long flags,
++                            unsigned long addr, unsigned long addr_only)
++{
++      if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) {
++              struct aa_profile *profile = aa_get_profile(current);
++              if (profile)
++                      /* future control check here */
++                      return -EACCES;
++              else
++                      return -EACCES;
++              aa_put_profile(profile);
++      }
++
++      return aa_mmap(file, "file_mmap", prot, flags);
++}
++
++static int apparmor_file_mprotect(struct vm_area_struct *vma,
++                                unsigned long reqprot, unsigned long prot)
++{
++      return aa_mmap(vma->vm_file, "file_mprotect", prot,
++                     !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
++}
++
++static int apparmor_task_alloc_security(struct task_struct *task)
++{
++      return aa_clone(task);
++}
++
++/*
++ * Called from IRQ context from RCU callback.
++ */
++static void apparmor_task_free_security(struct task_struct *task)
++{
++      aa_release(task);
++}
++
++static int apparmor_getprocattr(struct task_struct *task, char *name,
++                              char **value)
++{
++      unsigned len;
++      int error;
++      struct aa_profile *profile;
++
++      /* AppArmor only supports the "current" process attribute */
++      if (strcmp(name, "current") != 0)
++              return -EINVAL;
++
++      /* must be task querying itself or admin */
++      if (current != task && !capable(CAP_SYS_ADMIN))
++              return -EPERM;
++
++      profile = aa_get_profile(task);
++      error = aa_getprocattr(profile, value, &len);
++      aa_put_profile(profile);
++      if (!error)
++              error = len;
++
++      return error;
++}
++
++static int apparmor_setprocattr(struct task_struct *task, char *name,
++                              void *value, size_t size)
++{
++      char *command, *args;
++      int error;
++
++      if (strcmp(name, "current") != 0 || size == 0 || size >= PAGE_SIZE)
++              return -EINVAL;
++      args = value;
++      args[size] = '\0';
++      args = strstrip(args);
++      command = strsep(&args, " ");
++      if (!args)
++              return -EINVAL;
++      while (isspace(*args))
++              args++;
++      if (!*args)
++              return -EINVAL;
++
++      if (strcmp(command, "changehat") == 0) {
++              if (current != task)
++                      return -EACCES;
++              error = aa_setprocattr_changehat(args);
++      } else if (strcmp(command, "changeprofile") == 0) {
++              if (current != task)
++                      return -EACCES;
++              error = aa_setprocattr_changeprofile(args);
++      } else if (strcmp(command, "setprofile") == 0) {
++              struct aa_profile *profile;
++
++              /* Only an unconfined process with admin capabilities
++               * may change the profile of another task.
++               */
++
++              if (!capable(CAP_SYS_ADMIN))
++                      return -EACCES;
++
++              profile = aa_get_profile(current);
++              if (profile) {
++                      struct aa_audit sa;
++                      memset(&sa, 0, sizeof(sa));
++                      sa.operation = "profile_set";
++                      sa.gfp_mask = GFP_KERNEL;
++                      sa.task = task->pid;
++                      sa.info = "from confined process";
++                      aa_audit_reject(profile, &sa);
++                      aa_put_profile(profile);
++                      return -EACCES;
++              }
++              error = aa_setprocattr_setprofile(task, args);
++      } else {
++              struct aa_audit sa;
++              memset(&sa, 0, sizeof(sa));
++              sa.operation = "setprocattr";
++              sa.gfp_mask = GFP_KERNEL;
++              sa.info = "invalid command";
++              sa.name = command;
++              sa.task = task->pid;
++              aa_audit_reject(NULL, &sa);
++              return -EINVAL;
++      }
++
++      if (!error)
++              error = size;
++      return error;
++}
++
++struct security_operations apparmor_ops = {
++      .ptrace =                       apparmor_ptrace,
++      .capget =                       cap_capget,
++      .capset_check =                 cap_capset_check,
++      .capset_set =                   cap_capset_set,
++      .sysctl =                       apparmor_sysctl,
++      .capable =                      apparmor_capable,
++      .syslog =                       cap_syslog,
++
++      .netlink_send =                 cap_netlink_send,
++      .netlink_recv =                 cap_netlink_recv,
++
++      .bprm_apply_creds =             cap_bprm_apply_creds,
++      .bprm_set_security =            apparmor_bprm_set_security,
++      .bprm_secureexec =              apparmor_bprm_secureexec,
++
++      .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_symlink =                apparmor_inode_symlink,
++      .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,
++      .file_mprotect =                apparmor_file_mprotect,
++      .file_lock =                    apparmor_file_lock,
++
++      .task_alloc_security =          apparmor_task_alloc_security,
++      .task_free_security =           apparmor_task_free_security,
++      .task_post_setuid =             cap_task_post_setuid,
++      .task_reparent_to_init =        cap_task_reparent_to_init,
++
++      .getprocattr =                  apparmor_getprocattr,
++      .setprocattr =                  apparmor_setprocattr,
++};
++
++void info_message(const char *str)
++{
++      struct aa_audit sa;
++      memset(&sa, 0, sizeof(sa));
++      sa.gfp_mask = GFP_KERNEL;
++      sa.info = str;
++      printk(KERN_INFO "AppArmor: %s\n", str);
++      if (audit_enabled)
++              aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS);
++}
++
++static int __init apparmor_init(void)
++{
++      int error;
++
++      if (!apparmor_enabled) {
++              info_message("AppArmor disabled by boottime parameter\n");
++              return 0;
++      }
++
++      if ((error = create_apparmorfs())) {
++              AA_ERROR("Unable to activate AppArmor filesystem\n");
++              goto createfs_out;
++      }
++
++      if ((error = alloc_default_namespace())){
++              AA_ERROR("Unable to allocate default profile namespace\n");
++              goto alloc_out;
++      }
++
++      if ((error = register_security(&apparmor_ops))) {
++              AA_ERROR("Unable to register AppArmor\n");
++              goto register_security_out;
++      }
++
++      /* Report that AppArmor successfully initialized */
++      apparmor_initialized = 1;
++      if (apparmor_complain)
++              info_message("AppArmor initialized: complainmode enabled");
++      else
++              info_message("AppArmor initialized");
++
++      return error;
++
++register_security_out:
++      free_default_namespace();
++
++alloc_out:
++      destroy_apparmorfs();
++
++createfs_out:
++      return error;
++
++}
++
++security_initcall(apparmor_init);
++
++void apparmor_disable(void)
++{
++      /* Remove and release all the profiles on the profile list. */
++      mutex_lock(&aa_interface_lock);
++      aa_profile_ns_list_release();
++
++      /* FIXME: cleanup profiles references on files */
++      free_default_namespace();
++
++      /*
++       * Delay for an rcu cycle to make sure that all active task
++       * context readers have finished, and all profiles have been
++       * freed by their rcu callbacks.
++       */
++      synchronize_rcu();
++
++      destroy_apparmorfs();
++      mutex_unlock(&aa_interface_lock);
++
++      apparmor_initialized = 0;
++
++      info_message("AppArmor protection removed");
++}
++
++MODULE_DESCRIPTION("AppArmor process confinement");
++MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org");
++MODULE_LICENSE("GPL");
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-main.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-main.diff
new file mode 100644 (file)
index 0000000..19d31d8
--- /dev/null
@@ -0,0 +1,1493 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: Main Part
+
+The underlying functions by which the AppArmor LSM hooks are implemented.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+
+---
+ security/apparmor/main.c | 1478 +++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 1478 insertions(+)
+
+--- /dev/null
++++ b/security/apparmor/main.c
+@@ -0,0 +1,1478 @@
++/*
++ *    Copyright (C) 2002-2007 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 <linux/security.h>
++#include <linux/namei.h>
++#include <linux/audit.h>
++#include <linux/mount.h>
++#include <linux/ptrace.h>
++
++#include "apparmor.h"
++
++#include "inline.h"
++
++/*
++ * Table of capability names: we generate it from capabilities.h.
++ */
++static const char *capability_names[] = {
++#include "capability_names.h"
++};
++
++struct aa_namespace *default_namespace;
++
++static int aa_inode_mode(struct inode *inode)
++{
++      /* if the inode doesn't exist the user is creating it */
++      if (!inode || current->fsuid == inode->i_uid)
++              return AA_USER_SHIFT;
++      return AA_OTHER_SHIFT;
++}
++
++int alloc_default_namespace(void)
++{
++      struct aa_namespace *ns;
++      char *name = kstrdup("default", GFP_KERNEL);
++      if (!name)
++              return -ENOMEM;
++      ns = alloc_aa_namespace(name);
++      if (!ns) {
++              kfree(name);
++              return -ENOMEM;
++      }
++
++      write_lock(&profile_ns_list_lock);
++      default_namespace = ns;
++      aa_get_namespace(ns);
++      list_add(&ns->list, &profile_ns_list);
++      write_unlock(&profile_ns_list_lock);
++
++      return 0;
++}
++
++void free_default_namespace(void)
++{
++      write_lock(&profile_ns_list_lock);
++      list_del_init(&default_namespace->list);
++      write_unlock(&profile_ns_list_lock);
++      aa_put_namespace(default_namespace);
++      default_namespace = NULL;
++}
++
++static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer,
++                                 int mask)
++{
++      const char unsafex[] = "upcn";
++      const char safex[] = "UPCN";
++      char *m = buffer;
++
++      if (mask & AA_EXEC_MMAP)
++              *m++ = 'm';
++      if (mask & MAY_READ)
++              *m++ = 'r';
++      if (mask & MAY_WRITE)
++              *m++ = 'w';
++      else if (mask & MAY_APPEND)
++              *m++ = 'a';
++      if (mask & MAY_EXEC) {
++              int index = AA_EXEC_INDEX(mask);
++              /* all indexes > 4 are also named transitions */
++              if (index > 4)
++                      index = 4;
++              if (index > 0) {
++                      if (mask & AA_EXEC_UNSAFE)
++                              *m++ = unsafex[index - 1];
++                      else
++                              *m++ = safex[index - 1];
++              }
++              if (mask & AA_EXEC_INHERIT)
++                      *m++ = 'i';
++              *m++ = 'x';
++      }
++      if (mask & AA_MAY_LINK)
++              *m++ = 'l';
++      if (mask & AA_MAY_LOCK)
++              *m++ = 'k';
++      *m++ = '\0';
++}
++
++static void aa_audit_file_mask(struct audit_buffer *ab, const char *name,
++                             int mask)
++{
++      char user[10], other[10];
++
++      aa_audit_file_sub_mask(ab, user,
++                             (mask & AA_USER_PERMS) >> AA_USER_SHIFT);
++      aa_audit_file_sub_mask(ab, other,
++                             (mask & AA_OTHER_PERMS) >> AA_OTHER_SHIFT);
++
++      audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
++}
++
++/**
++ * aa_audit - Log an audit event to the audit subsystem
++ * @profile: profile to check against
++ * @sa: audit event
++ * @audit_cxt: audit context to log message to
++ * @type: audit event number
++ */
++static int aa_audit_base(struct aa_profile *profile, struct aa_audit *sa,
++                       struct audit_context *audit_cxt, int type)
++{
++      struct audit_buffer *ab = NULL;
++
++      ab = audit_log_start(audit_cxt, sa->gfp_mask, type);
++
++      if (!ab) {
++              AA_ERROR("Unable to log event (%d) to audit subsys\n",
++                       type);
++               /* don't fail operations in complain mode even if logging
++                * fails */
++              return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM;
++      }
++
++      if (sa->operation)
++              audit_log_format(ab, "operation=\"%s\"", sa->operation);
++
++      if (sa->info) {
++              audit_log_format(ab, " info=\"%s\"", sa->info);
++              if (sa->error_code)
++                      audit_log_format(ab, " error=%d", sa->error_code);
++      }
++
++      if (sa->request_mask)
++              aa_audit_file_mask(ab, "requested_mask", sa->request_mask);
++
++      if (sa->denied_mask)
++              aa_audit_file_mask(ab, "denied_mask", sa->denied_mask);
++
++      if (sa->request_mask)
++              audit_log_format(ab, " fsuid=%d", current->fsuid);
++
++      if (sa->iattr) {
++              struct iattr *iattr = sa->iattr;
++
++              audit_log_format(ab, " attribute=\"%s%s%s%s%s%s%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 | ATTR_ATIME_SET) ?
++                              "atime," : "",
++                      iattr->ia_valid & (ATTR_MTIME | ATTR_MTIME_SET) ?
++                              "mtime," : "",
++                      iattr->ia_valid & ATTR_CTIME ? "ctime," : "");
++      }
++
++      if (sa->task)
++              audit_log_format(ab, " task=%d", sa->task);
++
++      if (sa->parent)
++              audit_log_format(ab, " parent=%d", sa->parent);
++
++      if (sa->name) {
++              audit_log_format(ab, " name=");
++              audit_log_untrustedstring(ab, sa->name);
++      }
++
++      if (sa->name2) {
++              audit_log_format(ab, " name2=");
++              audit_log_untrustedstring(ab, sa->name2);
++      }
++
++      audit_log_format(ab, " pid=%d", current->pid);
++
++      if (profile) {
++              audit_log_format(ab, " profile=");
++              audit_log_untrustedstring(ab, profile->name);
++
++              if (profile->ns != default_namespace) {
++                      audit_log_format(ab, " namespace=");
++                      audit_log_untrustedstring(ab, profile->ns->name);
++              }
++      }
++
++      audit_log_end(ab);
++
++      return type == AUDIT_APPARMOR_ALLOWED ? 0 : sa->error_code;
++}
++
++/**
++ * aa_audit_syscallreject - Log a syscall rejection to the audit subsystem
++ * @profile: profile to check against
++ * @gfp: memory allocation flags
++ * @msg: string describing syscall being rejected
++ */
++int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
++                         const char *msg)
++{
++      struct aa_audit sa;
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "syscall";
++      sa.name = msg;
++      sa.gfp_mask = gfp;
++      sa.error_code = -EPERM;
++
++      return aa_audit_base(profile, &sa, current->audit_context,
++                           AUDIT_APPARMOR_DENIED);
++}
++
++int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
++                    int type)
++{
++      struct audit_context *audit_cxt;
++
++      audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
++      return aa_audit_base(profile, sa, audit_cxt, type);
++}
++
++void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa)
++{
++      aa_audit_message(profile, sa, AUDIT_APPARMOR_HINT);
++}
++
++void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa)
++{
++      aa_audit_message(profile, sa, AUDIT_APPARMOR_STATUS);
++}
++
++int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa)
++{
++      return aa_audit_message(profile, sa, AUDIT_APPARMOR_DENIED);
++}
++
++/**
++ * aa_audit - Log an audit event to the audit subsystem
++ * @profile: profile to check against
++ * @sa: audit event
++ */
++int aa_audit(struct aa_profile *profile, struct aa_audit *sa)
++{
++      int type = AUDIT_APPARMOR_DENIED;
++      struct audit_context *audit_cxt;
++
++      if (likely(!sa->error_code))
++              type = AUDIT_APPARMOR_AUDIT;
++      else if (PROFILE_COMPLAIN(profile))
++              type = AUDIT_APPARMOR_ALLOWED;
++
++      audit_cxt = apparmor_logsyscall ? current->audit_context : NULL;
++      return aa_audit_base(profile, sa, audit_cxt, type);
++}
++
++static int aa_audit_file(struct aa_profile *profile, struct aa_audit *sa)
++{
++      if (likely(!sa->error_code)) {
++              int mask = sa->audit_mask & AUDIT_FILE_MASK;
++
++              if (unlikely(PROFILE_AUDIT(profile)))
++                      mask |= AUDIT_FILE_MASK;
++
++              if (likely(!(sa->request_mask & mask)))
++                      return 0;
++
++              /* mask off perms that are not being force audited */
++              sa->request_mask &= mask | ALL_AA_EXEC_TYPE;
++      } else {
++              int mask = AUDIT_QUIET_MASK(sa->audit_mask);
++
++              if (!(sa->denied_mask & ~mask))
++                      return sa->error_code;
++
++              /* mask off perms whose denial is being silenced */
++              sa->denied_mask &= (~mask) | ALL_AA_EXEC_TYPE;
++      }
++
++      return aa_audit(profile, sa);
++}
++
++static int aa_audit_caps(struct aa_profile *profile, struct aa_audit *sa,
++                       int cap)
++{
++      if (likely(!sa->error_code)) {
++              if (likely(!PROFILE_AUDIT(profile) &&
++                         !cap_raised(profile->audit_caps, cap)))
++                      return 0;
++      }
++
++      /* quieting of capabilities is handled the caps_logged cache */
++      return aa_audit(profile, sa);
++}
++
++/**
++ * aa_file_denied - check for @mask access on a file
++ * @profile: profile to check against
++ * @name: pathname of file
++ * @mask: permission mask requested for file
++ * @audit_mask: return audit mask for the match
++ *
++ * Return %0 on success, or else the permissions in @mask that the
++ * profile denies.
++ */
++static int aa_file_denied(struct aa_profile *profile, const char *name,
++                        int mask, int *audit_mask)
++{
++      return (mask & ~aa_match(profile->file_rules, name, audit_mask));
++}
++
++/**
++ * aa_link_denied - check for permission to link a file
++ * @profile: profile to check against
++ * @link: pathname of link being created
++ * @target: pathname of target to be linked to
++ * @target_mode: UGO shift for target inode
++ * @request_mask: the permissions subset valid only if link succeeds
++ * @audit_mask: return the audit_mask for the link permission
++ * Return %0 on success, or else the permissions that the profile denies.
++ */
++static int aa_link_denied(struct aa_profile *profile, const char *link,
++                        const char *target, int target_mode,
++                        int *request_mask, int *audit_mask)
++{
++      unsigned int state;
++      int l_mode, t_mode, l_x, t_x, denied_mask = 0;
++      int link_mask = AA_MAY_LINK << target_mode;
++
++      *request_mask = link_mask;
++
++      l_mode = aa_match_state(profile->file_rules, DFA_START, link, &state);
++
++      if (l_mode & link_mask) {
++              int mode;
++              /* test to see if target can be paired with link */
++              state = aa_dfa_null_transition(profile->file_rules, state);
++              mode = aa_match_state(profile->file_rules, state, target,
++                                    &state);
++
++              if (!(mode & link_mask))
++                      denied_mask |= link_mask;
++
++              *audit_mask = dfa_audit_mask(profile->file_rules, state);
++
++              /* return if link subset test is not required */
++              if (!(mode & (AA_LINK_SUBSET_TEST << target_mode)))
++                      return denied_mask;
++      }
++
++      /* Do link perm subset test requiring permission on link are a
++       * subset of the permissions on target.
++       * If a subset test is required a permission subset test of the
++       * perms for the link are done against the user::other of the
++       * target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions.
++       *
++       * If the link has 'x', an exact match of all the execute flags
++       * must match.
++       */
++      denied_mask |= ~l_mode & link_mask;
++
++      t_mode = aa_match(profile->file_rules, target, NULL);
++
++      l_x = l_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS);
++      t_x = t_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS);
++
++      /* For actual subset test ignore valid-profile-transition flags,
++       * and link bits
++       */
++      l_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
++      t_mode &= AA_FILE_PERMS & ~AA_LINK_BITS;
++
++      *request_mask = l_mode | link_mask;
++
++      if (l_mode) {
++              int x = l_x | (t_x & ALL_AA_EXEC_UNSAFE);
++              denied_mask |= l_mode & ~t_mode;
++              /* mask off x modes not used by link */
++
++              /* handle exec subset
++               * - link safe exec issubset of unsafe exec
++               * - no link x perm is subset of target having x perm
++               */
++              if ((l_mode & AA_USER_EXEC) &&
++                  (x & AA_USER_EXEC_TYPE) != (t_x & AA_USER_EXEC_TYPE))
++                      denied_mask = AA_USER_EXEC | (l_x & AA_USER_EXEC_TYPE);
++              if ((l_mode & AA_OTHER_EXEC) &&
++                  (x & AA_OTHER_EXEC_TYPE) != (t_x & AA_OTHER_EXEC_TYPE))
++                      denied_mask = AA_OTHER_EXEC | (l_x & AA_OTHER_EXEC_TYPE);
++      }
++
++      return denied_mask;
++}
++
++/**
++ * aa_get_name - compute the pathname of a file
++ * @dentry: dentry of the file
++ * @mnt: vfsmount of the file
++ * @buffer: buffer that aa_get_name() allocated
++ * @check: AA_CHECK_DIR is set if the file is a directory
++ *
++ * Returns a pointer to the beginning of the pathname (which usually differs
++ * from the beginning of the buffer), or an error code.
++ *
++ * We need @check to indicate whether the file is a directory or not because
++ * the file may not yet exist, and so we cannot check the inode's file type.
++ */
++static char *aa_get_name(struct dentry *dentry, struct vfsmount *mnt,
++                       char **buffer, int check)
++{
++      char *name;
++      int is_dir, size = 256;
++
++      is_dir = (check & AA_CHECK_DIR) ? 1 : 0;
++
++      for (;;) {
++              char *buf = kmalloc(size, GFP_KERNEL);
++              if (!buf)
++                      return ERR_PTR(-ENOMEM);
++
++              name = d_namespace_path(dentry, mnt, buf, size - is_dir);
++              if (!IS_ERR(name)) {
++                      if (name[0] != '/') {
++                              /*
++                               * This dentry is not connected to the
++                               * namespace root -- reject access.
++                               */
++                              kfree(buf);
++                              return ERR_PTR(-ENOENT);
++                      }
++                      if (is_dir && name[1] != '\0') {
++                              /*
++                               * Append "/" to the pathname. The root
++                               * directory is a special case; it already
++                               * ends in slash.
++                               */
++                              buf[size - 2] = '/';
++                              buf[size - 1] = '\0';
++                      }
++
++                      *buffer = buf;
++                      return name;
++              }
++              if (PTR_ERR(name) != -ENAMETOOLONG)
++                      return name;
++
++              kfree(buf);
++              size <<= 1;
++              if (size > apparmor_path_max)
++                      return ERR_PTR(-ENAMETOOLONG);
++      }
++}
++
++static char *new_compound_name(const char *n1, const char *n2)
++{
++      char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL);
++      if (name)
++              sprintf(name, "%s//%s", n1, n2);
++      return name;
++}
++static inline void aa_put_name_buffer(char *buffer)
++{
++      kfree(buffer);
++}
++
++/**
++ * aa_perm_dentry - check if @profile allows @mask for a file
++ * @profile: profile to check against
++ * @dentry: dentry of the file
++ * @mnt: vfsmount o the file
++ * @sa: audit context
++ * @mask: requested profile permissions
++ * @check: kind of check to perform
++ *
++ * Returns 0 upon success, or else an error code.
++ *
++ * @check indicates the file type, and whether the file was accessed through
++ * an open file descriptor (AA_CHECK_FD) or not.
++ */
++static int aa_perm_dentry(struct aa_profile *profile, struct dentry *dentry,
++                        struct vfsmount *mnt, struct aa_audit *sa, int check)
++{
++      int error;
++      char *buffer = NULL;
++
++      sa->name = aa_get_name(dentry, mnt, &buffer, check);
++      sa->request_mask <<= aa_inode_mode(dentry->d_inode);
++      if (IS_ERR(sa->name)) {
++              /*
++               * deleted files are given a pass on permission checks when
++               * accessed through a file descriptor.
++               */
++              if (PTR_ERR(sa->name) == -ENOENT && (check & AA_CHECK_FD))
++                      sa->denied_mask = 0;
++              else {
++                      sa->denied_mask = sa->request_mask;
++                      sa->error_code = PTR_ERR(sa->name);
++                      if (sa->error_code == -ENOENT)
++                              sa->info = "Failed name resolution - object not a valid entry";
++                      else if (sa->error_code == -ENAMETOOLONG)
++                              sa->info = "Failed name resolution - name too long";
++                      else
++                              sa->info = "Failed name resolution";
++              }
++              sa->name = NULL;
++      } else
++              sa->denied_mask = aa_file_denied(profile, sa->name,
++                                               sa->request_mask,
++                                               &sa->audit_mask);
++
++      if (!sa->denied_mask)
++              sa->error_code = 0;
++
++      error = aa_audit_file(profile, sa);
++      aa_put_name_buffer(buffer);
++
++      return error;
++}
++
++/**
++ * aa_attr - check if attribute change is allowed
++ * @profile: profile to check against
++ * @dentry: dentry of the file to check
++ * @mnt: vfsmount of the file to check
++ * @iattr: attribute changes requested
++ */
++int aa_attr(struct aa_profile *profile, struct dentry *dentry,
++          struct vfsmount *mnt, struct iattr *iattr)
++{
++      struct inode *inode = dentry->d_inode;
++      int error, check;
++      struct aa_audit sa;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "setattr";
++      sa.gfp_mask = GFP_KERNEL;
++      sa.iattr = iattr;
++      sa.request_mask = MAY_WRITE;
++      sa.error_code = -EACCES;
++
++      check = 0;
++      if (inode && S_ISDIR(inode->i_mode))
++              check |= AA_CHECK_DIR;
++      if (iattr->ia_valid & ATTR_FILE)
++              check |= AA_CHECK_FD;
++
++      error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
++
++      return error;
++}
++
++/**
++ * aa_perm_xattr - check if xattr attribute change is allowed
++ * @profile: profile to check against
++ * @dentry: dentry of the file to check
++ * @mnt: vfsmount of the file to check
++ * @operation: xattr operation being done
++ * @mask: access mode requested
++ * @check: kind of check to perform
++ */
++int aa_perm_xattr(struct aa_profile *profile, const char *operation,
++                struct dentry *dentry, struct vfsmount *mnt, int mask,
++                int check)
++{
++      struct inode *inode = dentry->d_inode;
++      int error;
++      struct aa_audit sa;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = operation;
++      sa.gfp_mask = GFP_KERNEL;
++      sa.request_mask = mask;
++      sa.error_code = -EACCES;
++
++      if (inode && S_ISDIR(inode->i_mode))
++              check |= AA_CHECK_DIR;
++
++      error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
++
++      return error;
++}
++
++/**
++ * aa_perm - basic apparmor permissions check
++ * @profile: profile to check against
++ * @dentry: dentry of the file to check
++ * @mnt: vfsmount of the file to check
++ * @mask: access mode requested
++ * @check: kind of check to perform
++ *
++ * Determine if access @mask for the file is authorized by @profile.
++ * Returns 0 on success, or else an error code.
++ */
++int aa_perm(struct aa_profile *profile, const char *operation,
++          struct dentry *dentry, struct vfsmount *mnt, int mask, int check)
++{
++      struct aa_audit sa;
++      int error = 0;
++
++      if (mask == 0)
++              goto out;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = operation;
++      sa.gfp_mask = GFP_KERNEL;
++      sa.request_mask = mask;
++      sa.error_code = -EACCES;
++
++      error = aa_perm_dentry(profile, dentry, mnt, &sa, check);
++
++out:
++      return error;
++}
++
++/**
++ * aa_perm_dir
++ * @profile: profile to check against
++ * @dentry: dentry of directory to check
++ * @mnt: vfsmount of directory to check
++ * @operation: directory operation being performed
++ * @mask: access mode requested
++ *
++ * Determine if directory operation (make/remove) for dentry is authorized
++ * by @profile.
++ * Returns 0 on success, or else an error code.
++ */
++int aa_perm_dir(struct aa_profile *profile, const char *operation,
++              struct dentry *dentry, struct vfsmount *mnt, int mask)
++{
++      struct aa_audit sa;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = operation;
++      sa.gfp_mask = GFP_KERNEL;
++      sa.request_mask = mask;
++      sa.error_code = -EACCES;
++
++      return aa_perm_dentry(profile, dentry, mnt, &sa, AA_CHECK_DIR);
++}
++
++int aa_perm_path(struct aa_profile *profile, const char *operation,
++               const char *name, int mask, uid_t uid)
++{
++      struct aa_audit sa;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = operation;
++      sa.gfp_mask = GFP_KERNEL;
++      sa.request_mask = mask;
++      sa.name = name;
++      if (current->fsuid == uid)
++              sa.request_mask = mask << AA_USER_SHIFT;
++      else
++              sa.request_mask = mask << AA_OTHER_SHIFT;
++
++      sa.denied_mask = aa_file_denied(profile, name, sa.request_mask,
++                                      &sa.audit_mask) ;
++      sa.error_code = sa.denied_mask ? -EACCES : 0;
++
++      return aa_audit_file(profile, &sa);
++}
++
++/**
++ * aa_capability - test permission to use capability
++ * @cxt: aa_task_context with profile to check against
++ * @cap: capability to be tested
++ *
++ * Look up capability in profile capability set.
++ * Returns 0 on success, or else an error code.
++ */
++int aa_capability(struct aa_task_context *cxt, int cap)
++{
++      int error = cap_raised(cxt->profile->capabilities, cap) ? 0 : -EPERM;
++      struct aa_audit sa;
++
++      /* test if cap has alread been logged */
++      if (cap_raised(cxt->caps_logged, cap)) {
++              if (PROFILE_COMPLAIN(cxt->profile))
++                      error = 0;
++              return error;
++      } else
++              /* don't worry about rcu replacement of the cxt here.
++               * caps_logged is a cache to reduce the occurence of
++               * duplicate messages in the log.  The worst that can
++               * happen is duplicate capability messages shows up in
++               * the audit log
++               */
++              cap_raise(cxt->caps_logged, cap);
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "capable";
++      sa.gfp_mask = GFP_ATOMIC;
++      sa.name = capability_names[cap];
++      sa.error_code = error;
++
++      error = aa_audit_caps(cxt->profile, &sa, cap);
++
++      return error;
++}
++
++/* must be used inside rcu_read_lock or task_lock */
++int aa_may_ptrace(struct aa_task_context *cxt, struct aa_profile *tracee)
++{
++      if (!cxt || cxt->profile == tracee)
++              return 0;
++      return aa_capability(cxt, CAP_SYS_PTRACE);
++}
++
++/**
++ * aa_link - hard link check
++ * @profile: profile to check against
++ * @link: dentry of link being created
++ * @link_mnt: vfsmount of link being created
++ * @target: dentry of link target
++ * @target_mnt: vfsmunt of link target
++ *
++ * Returns 0 on success, or else an error code.
++ */
++int aa_link(struct aa_profile *profile,
++          struct dentry *link, struct vfsmount *link_mnt,
++          struct dentry *target, struct vfsmount *target_mnt)
++{
++      int error;
++      struct aa_audit sa;
++      char *buffer = NULL, *buffer2 = NULL;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "inode_link";
++      sa.gfp_mask = GFP_KERNEL;
++      sa.name = aa_get_name(link, link_mnt, &buffer, 0);
++      sa.name2 = aa_get_name(target, target_mnt, &buffer2, 0);
++
++      if (IS_ERR(sa.name)) {
++              sa.error_code = PTR_ERR(sa.name);
++              sa.name = NULL;
++      }
++      if (IS_ERR(sa.name2)) {
++              sa.error_code = PTR_ERR(sa.name2);
++              sa.name2 = NULL;
++      }
++
++      if (sa.name && sa.name2) {
++              sa.denied_mask = aa_link_denied(profile, sa.name, sa.name2,
++                                              aa_inode_mode(target->d_inode),
++                                              &sa.request_mask,
++                                              &sa.audit_mask);
++              sa.error_code = sa.denied_mask ? -EACCES : 0;
++      }
++
++      error = aa_audit_file(profile, &sa);
++
++      aa_put_name_buffer(buffer);
++      aa_put_name_buffer(buffer2);
++
++      return error;
++}
++
++/*******************************
++ * Global task related functions
++ *******************************/
++
++/**
++ * aa_clone - initialize the task context for a new task
++ * @child: task that is being created
++ *
++ * Returns 0 on success, or else an error code.
++ */
++int aa_clone(struct task_struct *child)
++{
++      struct aa_task_context *cxt, *child_cxt;
++      struct aa_profile *profile;
++
++      if (!aa_task_context(current))
++              return 0;
++      child_cxt = aa_alloc_task_context(GFP_KERNEL);
++      if (!child_cxt)
++              return -ENOMEM;
++
++repeat:
++      profile = aa_get_profile(current);
++      if (profile) {
++              lock_profile(profile);
++              cxt = aa_task_context(current);
++              if (unlikely(profile->isstale || !cxt ||
++                           cxt->profile != profile)) {
++                      /**
++                       * Race with profile replacement or removal, or with
++                       * task context removal.
++                       */
++                      unlock_profile(profile);
++                      aa_put_profile(profile);
++                      goto repeat;
++              }
++
++              /* No need to grab the child's task lock here. */
++              aa_change_task_context(child, child_cxt, profile,
++                                     cxt->cookie, cxt->previous_profile);
++              unlock_profile(profile);
++
++              if (APPARMOR_COMPLAIN(child_cxt) &&
++                  profile == profile->ns->null_complain_profile) {
++                      struct aa_audit sa;
++                      memset(&sa, 0, sizeof(sa));
++                      sa.operation = "clone";
++                      sa.gfp_mask = GFP_KERNEL;
++                      sa.task = child->pid;
++                      aa_audit_hint(profile, &sa);
++              }
++              aa_put_profile(profile);
++      } else
++              aa_free_task_context(child_cxt);
++
++      return 0;
++}
++
++static struct aa_profile *
++aa_register_find(struct aa_profile *profile, const char* ns_name,
++               const char *name, int mandatory, int complain,
++               struct aa_audit *sa)
++{
++      struct aa_namespace *ns;
++      struct aa_profile *new_profile;
++      int ns_ref = 0;
++
++      if (profile)
++              ns = profile->ns;
++      else
++              ns = default_namespace;
++
++      if (ns_name) {
++              /* locate the profile namespace */
++              ns = aa_find_namespace(ns_name);
++              if (!ns) {
++                      if (mandatory) {
++                              sa->info = "profile namespace not found";
++                              sa->denied_mask = sa->request_mask;
++                              sa->error_code = -ENOENT;
++                              return ERR_PTR(-ENOENT);
++                      } else {
++                              return NULL;
++                      }
++              }
++              ns_ref++;
++      }
++
++      /* Locate new profile */
++      new_profile = aa_find_profile(ns, name);
++
++      if (new_profile) {
++              AA_DEBUG("%s: setting profile %s\n",
++                       __FUNCTION__, new_profile->name);
++      } else if (mandatory && profile) {
++              sa->info = "mandatory profile missing";
++              sa->denied_mask = sa->request_mask;     /* shifted MAY_EXEC */
++              if (complain) {
++                      aa_audit_hint(profile, sa);
++                      new_profile =
++                          aa_dup_profile(profile->ns->null_complain_profile);
++              } else {
++                      sa->error_code = -EACCES;
++                      if (ns_ref)
++                              aa_put_namespace(ns);
++                      return ERR_PTR(-EACCES);
++              }
++      } else {
++              /* Only way we can get into this code is if task
++               * is unconfined, pix, nix.
++               */
++              AA_DEBUG("%s: No profile found for exec image '%s'\n",
++                       __FUNCTION__,
++                       name);
++      }
++      if (ns_ref)
++              aa_put_namespace(ns);
++      return new_profile;
++}
++
++static struct aa_profile *
++aa_x_to_profile(struct aa_profile *profile, const char *filename, int xmode,
++              struct aa_audit *sa, char **child)
++{
++      struct aa_profile *new_profile = NULL;
++      int ix = xmode & AA_EXEC_INHERIT;
++      int complain = PROFILE_COMPLAIN(profile);
++      int index;
++
++      *child = NULL;
++      switch (xmode & AA_EXEC_MODIFIERS) {
++      case 0:
++              /* only valid with ix flag */
++              ix = 1;
++              break;
++      case AA_EXEC_UNCONFINED:
++              /* only valid without ix flag */
++              ix = 0;
++              break;
++      case AA_EXEC_PROFILE:
++              new_profile = aa_register_find(profile, NULL, filename, !ix,
++                                             complain, sa);
++              break;
++      case AA_EXEC_CHILD:
++              *child = new_compound_name(profile->name, filename);
++              sa->name2 = *child;
++              if (!*child) {
++                      sa->info = "Failed name resolution - exec failed";
++                      sa->error_code = -ENOMEM;
++                      new_profile = ERR_PTR(-ENOMEM);
++              } else {
++                      new_profile = aa_register_find(profile, NULL, *child,
++                                                     !ix, complain, sa);
++              }
++              break;
++      default:
++              /* all other indexes are named transitions */
++              index = AA_EXEC_INDEX(xmode);
++              if (index - 4 > profile->exec_table_size) {
++                      sa->info = "invalid named transition - exec failed";
++                      sa->error_code = -EACCES;
++                      new_profile = ERR_PTR(-EACCES);
++              } else {
++                      char *ns_name = NULL;
++                      char *name = profile->exec_table[index - 4];
++                      if (*name == ':') {
++                              ns_name = name + 1;
++                              name = ns_name + strlen(ns_name) + 1;
++                      }
++                      sa->name2 = name;
++                      sa->name3 = ns_name;
++                      new_profile =
++                              aa_register_find(profile, ns_name, name,
++                                               !ix, complain, sa);
++              }
++      }
++      if (IS_ERR(new_profile))
++              /* all these failures must be audited - no quieting */
++              return ERR_PTR(aa_audit_reject(profile, sa));
++      return new_profile;
++}
++
++/**
++ * aa_register - register a new program
++ * @bprm: binprm of program being registered
++ *
++ * Try to register a new program during execve().  This should give the
++ * new program a valid aa_task_context if confined.
++ */
++int aa_register(struct linux_binprm *bprm)
++{
++      const char *filename;
++      char  *buffer = NULL, *child = NULL;
++      struct file *filp = bprm->file;
++      struct aa_profile *profile, *old_profile, *new_profile = NULL;
++      int exec_mode, complain = 0, shift;
++      struct aa_audit sa;
++
++      AA_DEBUG("%s\n", __FUNCTION__);
++
++      profile = aa_get_profile(current);
++
++      shift = aa_inode_mode(filp->f_dentry->d_inode);
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "exec";
++      sa.gfp_mask = GFP_KERNEL;
++      sa.request_mask = MAY_EXEC << shift;
++
++      filename = aa_get_name(filp->f_dentry, filp->f_vfsmnt, &buffer, 0);
++      if (IS_ERR(filename)) {
++              if (profile) {
++                      sa.info = "Failed name resolution - exec failed";
++                      sa.error_code = PTR_ERR(filename);
++                      aa_audit_file(profile, &sa);
++                      return sa.error_code;
++              } else
++                      return 0;
++      }
++      sa.name = filename;
++
++      exec_mode = AA_EXEC_UNSAFE << shift;
++
++repeat:
++      if (profile) {
++              complain = PROFILE_COMPLAIN(profile);
++
++              /* Confined task, determine what mode inherit, unconfined or
++               * mandatory to load new profile
++               */
++              exec_mode = aa_match(profile->file_rules, filename,
++                                   &sa.audit_mask);
++
++
++              if (exec_mode & sa.request_mask) {
++                      int xm = exec_mode >> shift;
++                      new_profile = aa_x_to_profile(profile, filename,
++                                                    xm, &sa, &child);
++
++                      if (!new_profile && (xm & AA_EXEC_INHERIT))
++                              /* (p|c|n|)ix - don't change profile */
++                              goto cleanup;
++                      /* error case caught below */
++
++              } else if (sa.request_mask & AUDIT_QUIET_MASK(sa.audit_mask)) {
++                      /* quiet failed exit */
++                      new_profile = ERR_PTR(-EACCES);
++              } else if (complain) {
++                      /* There was no entry in calling profile
++                       * describing mode to execute image in.
++                       * Drop into null-profile (disabling secure exec).
++                       */
++                      new_profile =
++                          aa_dup_profile(profile->ns->null_complain_profile);
++                      exec_mode |= AA_EXEC_UNSAFE << shift;
++              } else {
++                      sa.denied_mask = sa.request_mask;
++                      sa.error_code = -EACCES;
++                      new_profile = ERR_PTR(aa_audit_file(profile, &sa));
++              }
++      } else {
++              /* Unconfined task, load profile if it exists */
++              new_profile = aa_register_find(NULL, NULL, filename, 0, 0, &sa);
++              if (new_profile == NULL)
++                      goto cleanup;
++      }
++
++      if (IS_ERR(new_profile))
++              goto cleanup;
++
++      old_profile = __aa_replace_profile(current, new_profile);
++      if (IS_ERR(old_profile)) {
++              aa_put_profile(new_profile);
++              aa_put_profile(profile);
++              if (PTR_ERR(old_profile) == -ESTALE) {
++                      profile = aa_get_profile(current);
++                      goto repeat;
++              }
++              if (PTR_ERR(old_profile) == -EPERM) {
++                      sa.denied_mask = sa.request_mask;
++                      sa.info = "unable to set profile due to ptrace";
++                      sa.task = current->parent->pid;
++                      aa_audit_reject(profile, &sa);
++              }
++              new_profile = old_profile;
++              goto cleanup;
++      }
++      aa_put_profile(old_profile);
++      aa_put_profile(profile);
++
++      /* Handle confined exec.
++       * Can be at this point for the following reasons:
++       * 1. unconfined switching to confined
++       * 2. confined switching to different confinement
++       * 3. confined switching to unconfined
++       *
++       * Cases 2 and 3 are marked as requiring secure exec
++       * (unless policy specified "unsafe exec")
++       */
++      if (!(exec_mode & (AA_EXEC_UNSAFE << shift))) {
++              unsigned long bprm_flags;
++
++              bprm_flags = AA_SECURE_EXEC_NEEDED;
++              bprm->security = (void*)
++                      ((unsigned long)bprm->security | bprm_flags);
++      }
++
++      if (complain && new_profile &&
++          new_profile == new_profile->ns->null_complain_profile) {
++              sa.request_mask = 0;
++              sa.name = NULL;
++              sa.info = "set profile";
++              aa_audit_hint(new_profile, &sa);
++      }
++
++cleanup:
++      aa_put_name_buffer(child);
++      aa_put_name_buffer(buffer);
++      if (IS_ERR(new_profile))
++              return PTR_ERR(new_profile);
++      aa_put_profile(new_profile);
++      return 0;
++}
++
++/**
++ * aa_release - release a task context
++ * @task: task being released
++ *
++ * This is called after a task has exited and the parent has reaped it.
++ */
++void aa_release(struct task_struct *task)
++{
++      struct aa_task_context *cxt;
++      struct aa_profile *profile;
++      /*
++       * While the task context is still on a profile's task context
++       * list, another process could replace the profile under us,
++       * leaving us with a locked profile that is no longer attached
++       * to this task. So after locking the profile, we check that
++       * the profile is still attached.  The profile lock is
++       * sufficient to prevent the replacement race so we do not lock
++       * the task.
++       *
++       * Use lock subtyping to avoid lockdep reporting a false irq
++       * possible inversion between the task_lock and profile_lock
++       *
++       * We also avoid taking the task_lock here because lock_dep
++       * would report another false {softirq-on-W} potential irq_lock
++       * inversion.
++       *
++       * If the task does not have a profile attached we are safe;
++       * nothing can race with us at this point.
++       */
++
++repeat:
++      profile = aa_get_profile(task);
++      if (profile) {
++              lock_profile_nested(profile, aa_lock_task_release);
++              cxt = aa_task_context(task);
++              if (unlikely(!cxt || cxt->profile != profile)) {
++                      unlock_profile(profile);
++                      aa_put_profile(profile);
++                      goto repeat;
++              }
++              aa_change_task_context(task, NULL, NULL, 0, NULL);
++              unlock_profile(profile);
++              aa_put_profile(profile);
++      }
++}
++
++static int do_change_profile(struct aa_profile *expected,
++                           struct aa_namespace *ns, const char *name,
++                           u64 cookie, int restore, int hat,
++                           struct aa_audit *sa)
++{
++      struct aa_profile *new_profile = NULL, *old_profile = NULL,
++              *previous_profile = NULL;
++      struct aa_task_context *new_cxt, *cxt;
++      int error = 0;
++
++      sa->name = name;
++
++      new_cxt = aa_alloc_task_context(GFP_KERNEL);
++      if (!new_cxt)
++              return -ENOMEM;
++
++      new_profile = aa_find_profile(ns, name);
++      if (!new_profile && !restore) {
++              if (!PROFILE_COMPLAIN(expected)) {
++                      aa_free_task_context(new_cxt);
++                      return -ENOENT;
++              }
++              new_profile = aa_dup_profile(ns->null_complain_profile);
++      } else if (new_profile && hat && !PROFILE_IS_HAT(new_profile)) {
++              aa_free_task_context(new_cxt);
++              aa_put_profile(new_profile);
++              return error;
++      }
++
++      cxt = lock_task_and_profiles(current, new_profile);
++      if (!cxt) {
++              error = -EPERM;
++              goto out;
++      }
++      old_profile = cxt->profile;
++
++      if (cxt->profile != expected || (new_profile && new_profile->isstale)) {
++              error = -ESTALE;
++              goto out;
++      }
++
++      if (cxt->previous_profile) {
++              if (cxt->cookie != cookie) {
++                      error = -EACCES;
++                      sa->info = "killing process";
++                      aa_audit_reject(cxt->profile, sa);
++                      /* terminate process */
++                      (void)send_sig_info(SIGKILL, NULL, current);
++                      goto out;
++              }
++
++              if (!restore)
++                      previous_profile = cxt->previous_profile;
++      } else
++              previous_profile = cxt->profile;
++
++      if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, new_profile)) {
++              error = -EACCES;
++              goto out;
++      }
++
++      if (new_profile == ns->null_complain_profile)
++              aa_audit_hint(cxt->profile, sa);
++
++      if (APPARMOR_AUDIT(cxt))
++              aa_audit_message(cxt->profile, sa, AUDIT_APPARMOR_AUDIT);
++
++      if (!restore && cookie)
++              aa_change_task_context(current, new_cxt, new_profile, cookie,
++                                     previous_profile);
++      else
++              /* either return to previous_profile, or a permanent change */
++              aa_change_task_context(current, new_cxt, new_profile, 0, NULL);
++
++out:
++      if (aa_task_context(current) != new_cxt)
++              aa_free_task_context(new_cxt);
++      task_unlock(current);
++      unlock_both_profiles(old_profile, new_profile);
++      aa_put_profile(new_profile);
++      return error;
++}
++
++/**
++ * aa_change_profile - perform a one-way profile transition
++ * @ns_name: name of the profile namespace to change to
++ * @name: name of profile to change to
++ * Change to new profile @name.  Unlike with hats, there is no way
++ * to change back.
++ *
++ * Returns %0 on success, error otherwise.
++ */
++int aa_change_profile(const char *ns_name, const char *name)
++{
++      struct aa_task_context *cxt;
++      struct aa_profile *profile = NULL;
++      struct aa_namespace *ns = NULL;
++      struct aa_audit sa;
++      unsigned int state;
++      int error = -EINVAL;
++
++      if (!name)
++              return -EINVAL;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.gfp_mask = GFP_ATOMIC;
++      sa.operation = "change_profile";
++
++repeat:
++      task_lock(current);
++      cxt = aa_task_context(current);
++      if (cxt)
++              profile = aa_dup_profile(cxt->profile);
++      task_unlock(current);
++
++      if (ns_name)
++              ns = aa_find_namespace(ns_name);
++      else if (profile)
++              ns = aa_get_namespace(profile->ns);
++      else
++              ns = aa_get_namespace(default_namespace);
++
++      if (!ns) {
++              aa_put_profile(profile);
++              return -ENOENT;
++      }
++
++      if (!profile || PROFILE_COMPLAIN(profile) ||
++          (ns == profile->ns &&
++           (aa_match(profile->file_rules, name, NULL) & AA_CHANGE_PROFILE)))
++              error = do_change_profile(profile, ns, name, 0, 0, 0, &sa);
++      else {
++              /* check for a rule with a namespace prepended */
++              aa_match_state(profile->file_rules, DFA_START, ns->name,
++                             &state);
++              state = aa_dfa_null_transition(profile->file_rules, state);
++              if ((aa_match_state(profile->file_rules, state, name, NULL) &
++                    AA_CHANGE_PROFILE))
++                      error = do_change_profile(profile, ns, name, 0, 0, 0,
++                                                &sa);
++              else
++                      /* no permission to transition to profile @name */
++                      error = -EACCES;
++      }
++
++      aa_put_namespace(ns);
++      aa_put_profile(profile);
++      if (error == -ESTALE)
++              goto repeat;
++
++      return error;
++}
++
++/**
++ * aa_change_hat - change hat to/from subprofile
++ * @hat_name: hat to change to
++ * @cookie: magic value to validate the hat change
++ *
++ * Change to new @hat_name, and store the @hat_magic in the current task
++ * context.  If the new @hat_name is %NULL and the @cookie matches that
++ * stored in the current task context and is not 0, return to the top level
++ * profile.
++ * Returns %0 on success, error otherwise.
++ */
++int aa_change_hat(const char *hat_name, u64 cookie)
++{
++      struct aa_task_context *cxt;
++      struct aa_profile *profile, *previous_profile;
++      struct aa_audit sa;
++      int error = 0;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.gfp_mask = GFP_ATOMIC;
++      sa.operation = "change_hat";
++
++repeat:
++      task_lock(current);
++      cxt = aa_task_context(current);
++      if (!cxt) {
++              task_unlock(current);
++              return -EPERM;
++      }
++      profile = aa_dup_profile(cxt->profile);
++      previous_profile = aa_dup_profile(cxt->previous_profile);
++      task_unlock(current);
++
++      if (hat_name) {
++              char *name, *profile_name;
++
++              if (previous_profile)
++                      profile_name = previous_profile->name;
++              else
++                      profile_name = profile->name;
++
++              name = new_compound_name(profile_name, hat_name);
++              if (!name) {
++                      error = -ENOMEM;
++                      goto out;
++              }
++              error = do_change_profile(profile, profile->ns, name, cookie,
++                                        0, 1, &sa);
++              aa_put_name_buffer(name);
++      } else if (previous_profile)
++              error = do_change_profile(profile, profile->ns,
++                                        previous_profile->name, cookie, 1, 0,
++                                        &sa);
++      /* else ignore restores when there is no saved profile */
++
++out:
++      aa_put_profile(previous_profile);
++      aa_put_profile(profile);
++      if (error == -ESTALE)
++              goto repeat;
++
++      return error;
++}
++
++/**
++ * __aa_replace_profile - replace a task's profile
++ * @task: task to switch the profile of
++ * @profile: profile to switch to
++ *
++ * Returns a handle to the previous profile upon success, or else an
++ * error code.
++ */
++struct aa_profile *__aa_replace_profile(struct task_struct *task,
++                                      struct aa_profile *profile)
++{
++      struct aa_task_context *cxt, *new_cxt = NULL;
++      struct aa_profile *old_profile = NULL;
++
++      if (profile) {
++              new_cxt = aa_alloc_task_context(GFP_KERNEL);
++              if (!new_cxt)
++                      return ERR_PTR(-ENOMEM);
++      }
++
++      cxt = lock_task_and_profiles(task, profile);
++      if (unlikely(profile && profile->isstale)) {
++              task_unlock(task);
++              unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
++              aa_free_task_context(new_cxt);
++              return ERR_PTR(-ESTALE);
++      }
++
++      if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
++              task_unlock(task);
++              unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
++              aa_free_task_context(new_cxt);
++              return ERR_PTR(-EPERM);
++      }
++
++      if (cxt)
++              old_profile = aa_dup_profile(cxt->profile);
++      aa_change_task_context(task, new_cxt, profile, 0, NULL);
++
++      task_unlock(task);
++      unlock_both_profiles(profile, old_profile);
++      return old_profile;
++}
++
++/**
++ * lock_task_and_profiles - lock the task and confining profiles and @profile
++ * @task: task to lock
++ * @profile: extra profile to lock in addition to the current profile
++ *
++ * Handle the spinning on locking to make sure the task context and
++ * profile are consistent once all locks are aquired.
++ *
++ * return the aa_task_context currently confining the task.  The task lock
++ * will be held whether or not the task is confined.
++ */
++struct aa_task_context *
++lock_task_and_profiles(struct task_struct *task, struct aa_profile *profile)
++{
++      struct aa_task_context *cxt;
++      struct aa_profile *old_profile = NULL;
++
++      rcu_read_lock();
++repeat:
++      cxt = aa_task_context(task);
++      if (cxt)
++              old_profile = cxt->profile;
++
++      lock_both_profiles(profile, old_profile);
++      task_lock(task);
++
++      /* check for race with profile transition, replacement or removal */
++      if (unlikely(cxt != aa_task_context(task))) {
++              task_unlock(task);
++              unlock_both_profiles(profile, old_profile);
++              old_profile = NULL;
++              goto repeat;
++      }
++      rcu_read_unlock();
++      return cxt;
++}
++
++static void free_aa_task_context_rcu_callback(struct rcu_head *head)
++{
++      struct aa_task_context *cxt;
++
++      cxt = container_of(head, struct aa_task_context, rcu);
++      aa_free_task_context(cxt);
++}
++
++/**
++ * aa_change_task_context - switch a task to use a new context and profile
++ * @task: task that is having its task context changed
++ * @new_cxt: new task context to use after the switch
++ * @profile: new profile to use after the switch
++ * @cookie: magic value to switch to
++ * @previous_profile: profile the task can return to
++ */
++void aa_change_task_context(struct task_struct *task,
++                          struct aa_task_context *new_cxt,
++                          struct aa_profile *profile, u64 cookie,
++                          struct aa_profile *previous_profile)
++{
++      struct aa_task_context *old_cxt = aa_task_context(task);
++
++      if (old_cxt) {
++              list_del_init(&old_cxt->list);
++              call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
++      }
++      if (new_cxt) {
++              /* set the caps_logged cache to the quiet_caps mask
++               * this has the effect of quieting caps that are not
++               * supposed to be logged
++               */
++              new_cxt->caps_logged = profile->quiet_caps;
++              new_cxt->cookie = cookie;
++              new_cxt->task = task;
++              new_cxt->profile = aa_dup_profile(profile);
++              new_cxt->previous_profile = aa_dup_profile(previous_profile);
++              list_move(&new_cxt->list, &profile->task_contexts);
++      }
++      rcu_assign_pointer(task->security, new_cxt);
++}
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-misc.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-misc.diff
new file mode 100644 (file)
index 0000000..4670dbd
--- /dev/null
@@ -0,0 +1,1441 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: all the rest
+
+All the things that didn't nicely fit in a category on their own: kbuild
+code, declararions and inline functions, /sys/kernel/security/apparmor
+filesystem for controlling apparmor from user space, profile list
+functions, locking documentation, /proc/$pid/task/$tid/attr/current
+access.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+
+---
+ security/apparmor/Kconfig      |   42 ++++
+ security/apparmor/Makefile     |   13 +
+ security/apparmor/apparmor.h   |  371 +++++++++++++++++++++++++++++++++++++++++
+ security/apparmor/apparmorfs.c |  281 +++++++++++++++++++++++++++++++
+ security/apparmor/inline.h     |  250 +++++++++++++++++++++++++++
+ security/apparmor/list.c       |  174 +++++++++++++++++++
+ security/apparmor/locking.txt  |   68 +++++++
+ security/apparmor/procattr.c   |  195 +++++++++++++++++++++
+ 8 files changed, 1394 insertions(+)
+
+--- /dev/null
++++ b/security/apparmor/Kconfig
+@@ -0,0 +1,42 @@
++config SECURITY_APPARMOR
++      bool "AppArmor support"
++      depends on SECURITY
++      select AUDIT
++      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
++        <http://forge.novell.com/modules/xfmod/project/?apparmor>
++
++        If you are unsure how to answer this question, answer N.
++
++config SECURITY_APPARMOR_BOOTPARAM_VALUE
++      int "AppArmor boot parameter default value"
++      depends on SECURITY_APPARMOR
++      range 0 1
++      default 1
++      help
++        This option sets the default value for the kernel parameter
++        'apparmor', which allows AppArmor to be enabled or disabled
++          at boot.  If this option is set to 0 (zero), the AppArmor
++        kernel parameter will default to 0, disabling AppArmor at
++        bootup.  If this option is set to 1 (one), the AppArmor
++        kernel parameter will default to 1, enabling AppArmor at
++        bootup.
++
++        If you are unsure how to answer this question, answer 1.
++
++config SECURITY_APPARMOR_DISABLE
++      bool "AppArmor runtime disable"
++      depends on SECURITY_APPARMOR
++      default n
++      help
++        This option enables writing to a apparmorfs node 'disable', which
++        allows AppArmor to be disabled at runtime prior to the policy load.
++        AppArmor will then remain disabled until the next boot.
++        This option is similar to the apparmor.enabled=0 boot parameter,
++        but is to support runtime disabling of AppArmor, e.g. from
++        /sbin/init, for portability across platforms where boot
++        parameters are difficult to employ.
++
++        If you are unsure how to answer this question, answer N.
+--- /dev/null
++++ b/security/apparmor/Makefile
+@@ -0,0 +1,13 @@
++# Makefile for AppArmor Linux Security Module
++#
++obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
++
++apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \
++            module_interface.o match.o
++
++quiet_cmd_make-caps = GEN     $@
++cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
++
++$(obj)/main.o : $(obj)/capability_names.h
++$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
++      $(call cmd,make-caps)
+--- /dev/null
++++ b/security/apparmor/apparmor.h
+@@ -0,0 +1,371 @@
++/*
++ *    Copyright (C) 1998-2007 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 __APPARMOR_H
++#define __APPARMOR_H
++
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/binfmts.h>
++#include <linux/rcupdate.h>
++
++/*
++ * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
++ * for profile permissions
++ */
++#define AA_MAY_LINK                   0x0010
++#define AA_MAY_LOCK                   0x0020
++#define AA_EXEC_MMAP                  0x0040
++#define AA_MAY_MOUNT                  0x0080  /* no direct audit mapping */
++#define AA_EXEC_UNSAFE                        0x0100
++#define AA_EXEC_INHERIT                       0x0200
++#define AA_EXEC_MOD_0                 0x0400
++#define AA_EXEC_MOD_1                 0x0800
++#define AA_EXEC_MOD_2                 0x1000
++#define AA_EXEC_MOD_3                 0x2000
++
++#define AA_BASE_PERMS                 (MAY_READ | MAY_WRITE | MAY_EXEC | \
++                                       MAY_APPEND | AA_MAY_LINK | \
++                                       AA_MAY_LOCK | AA_EXEC_MMAP | \
++                                       AA_MAY_MOUNT | AA_EXEC_UNSAFE | \
++                                       AA_EXEC_INHERIT | AA_EXEC_MOD_0 | \
++                                       AA_EXEC_MOD_1 | AA_EXEC_MOD_2 | \
++                                       AA_EXEC_MOD_3)
++
++#define AA_EXEC_MODIFIERS             (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \
++                                       AA_EXEC_MOD_2 | AA_EXEC_MOD_3)
++
++#define AA_EXEC_TYPE                  (AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \
++                                       AA_EXEC_MODIFIERS)
++
++#define AA_EXEC_UNCONFINED            AA_EXEC_MOD_0
++#define AA_EXEC_PROFILE                       AA_EXEC_MOD_1
++#define AA_EXEC_CHILD                 (AA_EXEC_MOD_0 | AA_EXEC_MOD_1)
++/* remaining exec modes are index into profile name table */
++#define AA_EXEC_INDEX(mode)           ((mode & AA_EXEC_MODIFIERS) >> 10)
++
++#define AA_USER_SHIFT                 0
++#define AA_OTHER_SHIFT                        14
++
++#define AA_USER_PERMS                 (AA_BASE_PERMS << AA_USER_SHIFT)
++#define AA_OTHER_PERMS                        (AA_BASE_PERMS << AA_OTHER_SHIFT)
++
++#define AA_FILE_PERMS                 (AA_USER_PERMS | AA_OTHER_PERMS)
++
++#define AA_LINK_BITS                  ((AA_MAY_LINK << AA_USER_SHIFT) | \
++                                       (AA_MAY_LINK << AA_OTHER_SHIFT))
++
++#define AA_USER_EXEC                  (MAY_EXEC << AA_USER_SHIFT)
++#define AA_OTHER_EXEC                 (MAY_EXEC << AA_OTHER_SHIFT)
++
++#define AA_USER_EXEC_TYPE             (AA_EXEC_TYPE << AA_USER_SHIFT)
++#define AA_OTHER_EXEC_TYPE            (AA_EXEC_TYPE << AA_OTHER_SHIFT)
++
++#define AA_EXEC_BITS                  (AA_USER_EXEC | AA_OTHER_EXEC)
++
++#define ALL_AA_EXEC_UNSAFE            ((AA_EXEC_UNSAFE << AA_USER_SHIFT) | \
++                                       (AA_EXEC_UNSAFE << AA_OTHER_SHIFT))
++
++#define ALL_AA_EXEC_TYPE              (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE)
++
++/* overloaded permissions for link pairs */
++#define AA_LINK_SUBSET_TEST           0x0020
++
++#define AA_USER_PTRACE                        0x10000000
++#define AA_OTHER_PTRACE                       0x20000000
++#define AA_PTRACE_PERMS                       (AA_USER_PTRACE | AA_OTHER_PTRACE)
++
++/* shared permissions that are not duplicated in user::other */
++#define AA_CHANGE_HAT                 0x40000000
++#define AA_CHANGE_PROFILE             0x80000000
++
++#define AA_SHARED_PERMS                       (AA_CHANGE_HAT | AA_CHANGE_PROFILE)
++
++#define AA_VALID_PERM_MASK            (AA_FILE_PERMS | AA_PTRACE_PERMS | \
++                                       AA_SHARED_PERMS)
++
++/* audit bits for the second accept field */
++#define AUDIT_FILE_MASK 0x1fc07f
++#define AUDIT_QUIET_MASK(mask)                ((mask >> 7) & AUDIT_FILE_MASK)
++#define AA_VALID_PERM2_MASK           0x0fffffff
++
++#define AA_SECURE_EXEC_NEEDED         1
++
++/* 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;
++extern unsigned int apparmor_path_max;
++
++#define PROFILE_COMPLAIN(_profile) \
++      (apparmor_complain == 1 || ((_profile) && (_profile)->flags.complain))
++
++#define APPARMOR_COMPLAIN(_cxt) \
++      (apparmor_complain == 1 || \
++       ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.complain))
++
++#define PROFILE_AUDIT(_profile) \
++      (apparmor_audit == 1 || ((_profile) && (_profile)->flags.audit))
++
++#define APPARMOR_AUDIT(_cxt) \
++      (apparmor_audit == 1 || \
++       ((_cxt) && (_cxt)->profile && (_cxt)->profile->flags.audit))
++
++#define PROFILE_IS_HAT(_profile) \
++      ((_profile) && (_profile)->flags.hat)
++
++/*
++ * 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_ERROR(fmt, args...)        printk(KERN_ERR "AppArmor: " fmt, ##args)
++
++struct aa_profile;
++
++/* struct aa_namespace - namespace for a set of profiles
++ * @name: the name of the namespace
++ * @list: list the namespace is on
++ * @profiles: list of profile in the namespace
++ * @profile_count: the number of profiles in the namespace
++ * @null_complain_profile: special profile used for learning in this namespace
++ * @count: reference count on the namespace
++ * @lock: lock for adding/removing profile to the namespace
++ */
++struct aa_namespace {
++      char *name;
++      struct list_head list;
++      struct list_head profiles;
++      int profile_count;
++      struct aa_profile *null_complain_profile;
++
++      struct kref count;
++      rwlock_t lock;
++};
++
++/* struct aa_profile - basic confinement data
++ * @name: the profiles name
++ * @list: list this profile is on
++ * @ns: namespace the profile is in
++ * @file_rules: dfa containing the profiles file rules
++ * @flags: flags controlling profile behavior
++ * @isstale: flag indicating if profile is stale
++ * @set_caps: capabilities that are being set
++ * @capabilities: capabilities mask
++ * @audit_caps: caps that are to be audited
++ * @quiet_caps: caps that should not be audited
++ * @capabilities: capabilities granted by the process
++ * @count: reference count of the profile
++ * @task_contexts: list of tasks confined by profile
++ * @lock: lock for the task_contexts list
++ * @network_families: basic network permissions
++ * @audit_network: which network permissions to force audit
++ * @quiet_network: which network permissions to quiet rejects
++ *
++ * The AppArmor profile contains the basic confinement data.  Each profile
++ * has a name, and all nonstale profile are in a profile namespace.
++ *
++ * The task_contexts list and the isstale flag are protected by the
++ * profile lock.
++ *
++ * If a task context is moved between two profiles, we first need to grab
++ * both profile locks. lock_both_profiles() does that in a deadlock-safe
++ * way.
++ */
++struct aa_profile {
++      char *name;
++      struct list_head list;
++      struct aa_namespace *ns;
++
++      int exec_table_size;
++      char **exec_table;
++      struct aa_dfa *file_rules;
++      struct {
++              int hat;
++              int complain;
++              int audit;
++      } flags;
++      int isstale;
++
++      kernel_cap_t set_caps;
++      kernel_cap_t capabilities;
++      kernel_cap_t audit_caps;
++      kernel_cap_t quiet_caps;
++
++      struct kref count;
++      struct list_head task_contexts;
++      spinlock_t lock;
++      unsigned long int_flags;
++};
++
++extern struct list_head profile_ns_list;
++extern rwlock_t profile_ns_list_lock;
++extern struct mutex aa_interface_lock;
++
++/**
++ * struct aa_task_context - primary label for confined tasks
++ * @profile: the current profile
++ * @previous_profile: profile the task may return to
++ * @cookie: magic value the task must know for returning to @previous_profile
++ * @list: list this aa_task_context is on
++ * @task: task that the aa_task_context confines
++ * @rcu: rcu head used when freeing the aa_task_context
++ * @caps_logged: caps that have previously generated log entries
++ *
++ * Contains the task's current profile (which could change due to
++ * change_hat).  Plus the hat_magic needed during change_hat.
++ */
++struct aa_task_context {
++      struct aa_profile *profile;
++      struct aa_profile *previous_profile;
++      u64 cookie;
++      struct list_head list;
++      struct task_struct *task;
++      struct rcu_head rcu;
++      kernel_cap_t caps_logged;
++};
++
++extern struct aa_namespace *default_namespace;
++
++/* 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 {
++      const char *operation;
++      gfp_t gfp_mask;
++      const char *info;
++      const char *name;
++      const char *name2;
++      const char *name3;
++      int request_mask, denied_mask, audit_mask;
++      struct iattr *iattr;
++      pid_t task, parent;
++      int error_code;
++};
++
++/* Flags for the permission check functions */
++#define AA_CHECK_FD   1  /* coming from a file descriptor */
++#define AA_CHECK_DIR  2  /* file type is directory */
++
++/* lock subtypes so lockdep does not raise false dependencies */
++enum aa_lock_class {
++      aa_lock_normal,
++      aa_lock_nested,
++      aa_lock_task_release
++};
++
++/* main.c */
++extern int alloc_default_namespace(void);
++extern void free_default_namespace(void);
++extern int aa_audit_message(struct aa_profile *profile, struct aa_audit *sa,
++                          int type);
++void aa_audit_hint(struct aa_profile *profile, struct aa_audit *sa);
++void aa_audit_status(struct aa_profile *profile, struct aa_audit *sa);
++int aa_audit_reject(struct aa_profile *profile, struct aa_audit *sa);
++extern int aa_audit_syscallreject(struct aa_profile *profile, gfp_t gfp,
++                                const char *);
++extern int aa_audit(struct aa_profile *profile, struct aa_audit *);
++
++extern int aa_attr(struct aa_profile *profile, struct dentry *dentry,
++                 struct vfsmount *mnt, struct iattr *iattr);
++extern int aa_perm_xattr(struct aa_profile *profile, const char *operation,
++                       struct dentry *dentry, struct vfsmount *mnt,
++                       int mask, int check);
++extern int aa_capability(struct aa_task_context *cxt, int cap);
++extern int aa_perm(struct aa_profile *profile, const char *operation,
++                 struct dentry *dentry, struct vfsmount *mnt, int mask,
++                 int check);
++extern int aa_perm_dir(struct aa_profile *profile, const char *operation,
++                     struct dentry *dentry, struct vfsmount *mnt,
++                     int mask);
++extern int aa_perm_path(struct aa_profile *, const char *operation,
++                      const char *name, int mask, uid_t uid);
++extern int aa_link(struct aa_profile *profile,
++                 struct dentry *link, struct vfsmount *link_mnt,
++                 struct dentry *target, struct vfsmount *target_mnt);
++extern int aa_clone(struct task_struct *task);
++extern int aa_register(struct linux_binprm *bprm);
++extern void aa_release(struct task_struct *task);
++extern int aa_change_hat(const char *id, u64 hat_magic);
++extern int aa_change_profile(const char *ns_name, const char *name);
++extern struct aa_profile *__aa_replace_profile(struct task_struct *task,
++                                             struct aa_profile *profile);
++extern struct aa_task_context *lock_task_and_profiles(struct task_struct *task,
++                                                    struct aa_profile *profile);
++extern void unlock_task_and_profiles(struct task_struct *task,
++                                   struct aa_task_context *cxt,
++                                   struct aa_profile *profile);
++extern void aa_change_task_context(struct task_struct *task,
++                                 struct aa_task_context *new_cxt,
++                                 struct aa_profile *profile, u64 cookie,
++                                 struct aa_profile *previous_profile);
++extern int aa_may_ptrace(struct aa_task_context *cxt,
++                       struct aa_profile *tracee);
++
++/* lsm.c */
++extern int apparmor_initialized;
++extern void info_message(const char *str);
++extern void apparmor_disable(void);
++
++/* list.c */
++extern struct aa_namespace *__aa_find_namespace(const char *name,
++                                              struct list_head *list);
++extern struct aa_profile *__aa_find_profile(const char *name,
++                                          struct list_head *list);
++extern void aa_profile_ns_list_release(void);
++
++/* module_interface.c */
++extern ssize_t aa_add_profile(void *, size_t);
++extern ssize_t aa_replace_profile(void *, size_t);
++extern ssize_t aa_remove_profile(char *, size_t);
++extern struct aa_namespace *alloc_aa_namespace(char *name);
++extern void free_aa_namespace(struct aa_namespace *ns);
++extern void free_aa_namespace_kref(struct kref *kref);
++extern struct aa_profile *alloc_aa_profile(void);
++extern void free_aa_profile(struct aa_profile *profile);
++extern void free_aa_profile_kref(struct kref *kref);
++extern void aa_unconfine_tasks(struct aa_profile *profile);
++
++/* procattr.c */
++extern int aa_getprocattr(struct aa_profile *profile, char **string,
++                        unsigned *len);
++extern int aa_setprocattr_changehat(char *args);
++extern int aa_setprocattr_changeprofile(char *args);
++extern int aa_setprocattr_setprofile(struct task_struct *task, char *args);
++
++/* apparmorfs.c */
++extern int create_apparmorfs(void);
++extern void destroy_apparmorfs(void);
++
++/* match.c */
++extern struct aa_dfa *aa_match_alloc(void);
++extern void aa_match_free(struct aa_dfa *dfa);
++extern int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size);
++extern int verify_dfa(struct aa_dfa *dfa);
++extern unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *);
++extern unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
++                                    const char *str);
++extern unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
++                                 const char *str, unsigned int *final);
++extern unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
++                                         unsigned int start);
++
++#endif  /* __APPARMOR_H */
+--- /dev/null
++++ b/security/apparmor/apparmorfs.c
+@@ -0,0 +1,281 @@
++/*
++ *    Copyright (C) 1998-2007 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 <linux/security.h>
++#include <linux/vmalloc.h>
++#include <linux/module.h>
++#include <linux/seq_file.h>
++#include <asm/uaccess.h>
++#include <linux/namei.h>
++
++#include "apparmor.h"
++#include "inline.h"
++
++static char *aa_simple_write_to_buffer(const char __user *userbuf,
++                                     size_t alloc_size, size_t copy_size,
++                                     loff_t *pos, const char *operation)
++{
++      struct aa_profile *profile;
++      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.
++       */
++      profile = aa_get_profile(current);
++      if (profile) {
++              struct aa_audit sa;
++              memset(&sa, 0, sizeof(sa));
++              sa.operation = operation;
++              sa.gfp_mask = GFP_KERNEL;
++              sa.error_code = -EACCES;
++              data = ERR_PTR(aa_audit_reject(profile, &sa));
++              aa_put_profile(profile);
++              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;
++}
++
++/* apparmor/profiles */
++extern struct seq_operations apparmorfs_profiles_op;
++
++static int aa_profiles_open(struct inode *inode, struct file *file)
++{
++      return seq_open(file, &apparmorfs_profiles_op);
++}
++
++
++static int aa_profiles_release(struct inode *inode, struct file *file)
++{
++      return seq_release(inode, file);
++}
++
++static struct file_operations apparmorfs_profiles_fops = {
++      .open =         aa_profiles_open,
++      .read =         seq_read,
++      .llseek =       seq_lseek,
++      .release =      aa_profiles_release,
++};
++
++/* apparmor/matching */
++static ssize_t aa_matching_read(struct file *file, char __user *buf,
++                             size_t size, loff_t *ppos)
++{
++      const char *matching = "pattern=aadfa audit perms=rwxamlk/ user::other";
++
++      return simple_read_from_buffer(buf, size, ppos, matching,
++                                     strlen(matching));
++}
++
++static struct file_operations apparmorfs_matching_fops = {
++      .read =         aa_matching_read,
++};
++
++/* apparmor/features */
++static ssize_t aa_features_read(struct file *file, char __user *buf,
++                              size_t size, loff_t *ppos)
++{
++      const char *features = "file=3.0 capability=2.0 network=1.0 "
++                             "change_hat=1.5 change_profile=1.0 "
++                             "aanamespaces=1.0";
++
++      return simple_read_from_buffer(buf, size, ppos, features,
++                                     strlen(features));
++}
++
++static struct file_operations apparmorfs_features_fops = {
++      .read =         aa_features_read,
++};
++
++/* apparmor/.load */
++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, "profile_load");
++
++      error = PTR_ERR(data);
++      if (!IS_ERR(data)) {
++              error = aa_add_profile(data, size);
++              vfree(data);
++      }
++
++      return error;
++}
++
++
++static struct file_operations apparmorfs_profile_load = {
++      .write = aa_profile_load
++};
++
++/* apparmor/.replace */
++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,
++                                       "profile_replace");
++
++      error = PTR_ERR(data);
++      if (!IS_ERR(data)) {
++              error = aa_replace_profile(data, size);
++              vfree(data);
++      }
++
++      return error;
++}
++
++
++static struct file_operations apparmorfs_profile_replace = {
++      .write = aa_profile_replace
++};
++
++/* apparmor/.remove */
++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_remove_profile needs a null terminated string so 1 extra
++       * byte is allocated and the copied data is null terminated.
++       */
++      data = aa_simple_write_to_buffer(buf, size + 1, size, pos,
++                                       "profile_remove");
++
++      error = PTR_ERR(data);
++      if (!IS_ERR(data)) {
++              data[size] = 0;
++              error = aa_remove_profile(data, size);
++              vfree(data);
++      }
++
++      return error;
++}
++
++static struct file_operations apparmorfs_profile_remove = {
++      .write = aa_profile_remove
++};
++
++static struct dentry *apparmor_dentry;
++
++static void aafs_remove(const char *name)
++{
++      struct dentry *dentry;
++
++      dentry = lookup_one_len(name, apparmor_dentry, strlen(name));
++      if (!IS_ERR(dentry)) {
++              securityfs_remove(dentry);
++              dput(dentry);
++      }
++}
++
++static int aafs_create(const char *name, int mask, struct file_operations *fops)
++{
++      struct dentry *dentry;
++
++      dentry = securityfs_create_file(name, S_IFREG | mask, apparmor_dentry,
++                                      NULL, fops);
++
++      return IS_ERR(dentry) ? PTR_ERR(dentry) : 0;
++}
++
++void destroy_apparmorfs(void)
++{
++      if (apparmor_dentry) {
++              aafs_remove(".remove");
++              aafs_remove(".replace");
++              aafs_remove(".load");
++              aafs_remove("matching");
++              aafs_remove("features");
++              aafs_remove("profiles");
++              securityfs_remove(apparmor_dentry);
++              apparmor_dentry = NULL;
++      }
++}
++
++int create_apparmorfs(void)
++{
++      int error;
++
++      if (!apparmor_initialized)
++              return 0;
++
++      if (apparmor_dentry) {
++              AA_ERROR("%s: AppArmor securityfs already exists\n",
++                      __FUNCTION__);
++              return -EEXIST;
++      }
++
++      apparmor_dentry = securityfs_create_dir("apparmor", NULL);
++      if (IS_ERR(apparmor_dentry)) {
++              error = PTR_ERR(apparmor_dentry);
++              apparmor_dentry = NULL;
++              goto error;
++      }
++      error = aafs_create("profiles", 0440, &apparmorfs_profiles_fops);
++      if (error)
++              goto error;
++      error = aafs_create("matching", 0444, &apparmorfs_matching_fops);
++      if (error)
++              goto error;
++      error = aafs_create("features", 0444, &apparmorfs_features_fops);
++      if (error)
++              goto error;
++      error = aafs_create(".load", 0640, &apparmorfs_profile_load);
++      if (error)
++              goto error;
++      error = aafs_create(".replace", 0640, &apparmorfs_profile_replace);
++      if (error)
++              goto error;
++      error = aafs_create(".remove", 0640, &apparmorfs_profile_remove);
++      if (error)
++              goto error;
++
++      /* Report that AppArmor fs is enabled */
++      info_message("AppArmor Filesystem Enabled");
++      return 0;
++
++error:
++      destroy_apparmorfs();
++      AA_ERROR("Error creating AppArmor securityfs\n");
++      apparmor_disable();
++      return error;
++}
++
++fs_initcall(create_apparmorfs);
++
+--- /dev/null
++++ b/security/apparmor/inline.h
+@@ -0,0 +1,250 @@
++/*
++ *    Copyright (C) 1998-2007 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 <linux/sched.h>
++
++#include "match.h"
++
++static inline int mediated_filesystem(struct inode *inode)
++{
++      return !(inode->i_sb->s_flags & MS_NOUSER);
++}
++
++static inline struct aa_task_context *aa_task_context(struct task_struct *task)
++{
++      return (struct aa_task_context *) rcu_dereference(task->security);
++}
++
++static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
++{
++      if (ns)
++              kref_get(&(ns->count));
++
++      return ns;
++}
++
++static inline void aa_put_namespace(struct aa_namespace *ns)
++{
++      if (ns)
++              kref_put(&ns->count, free_aa_namespace_kref);
++}
++
++
++static inline struct aa_namespace *aa_find_namespace(const char *name)
++{
++      struct aa_namespace *ns = NULL;
++
++      read_lock(&profile_ns_list_lock);
++      ns = aa_get_namespace(__aa_find_namespace(name, &profile_ns_list));
++      read_unlock(&profile_ns_list_lock);
++
++      return ns;
++}
++
++/**
++ * aa_dup_profile - increment refcount on profile @p
++ * @p: profile
++ */
++static inline struct aa_profile *aa_dup_profile(struct aa_profile *p)
++{
++      if (p)
++              kref_get(&(p->count));
++
++      return p;
++}
++
++/**
++ * aa_put_profile - decrement refcount on profile @p
++ * @p: profile
++ */
++static inline void aa_put_profile(struct aa_profile *p)
++{
++      if (p)
++              kref_put(&p->count, free_aa_profile_kref);
++}
++
++static inline struct aa_profile *aa_get_profile(struct task_struct *task)
++{
++      struct aa_task_context *cxt;
++      struct aa_profile *profile = NULL;
++
++      rcu_read_lock();
++      cxt = aa_task_context(task);
++      if (cxt) {
++              profile = cxt->profile;
++              aa_dup_profile(profile);
++      }
++      rcu_read_unlock();
++
++      return profile;
++}
++
++static inline struct aa_profile *aa_find_profile(struct aa_namespace *ns,
++                                               const char *name)
++{
++      struct aa_profile *profile = NULL;
++
++      read_lock(&ns->lock);
++      profile = aa_dup_profile(__aa_find_profile(name, &ns->profiles));
++      read_unlock(&ns->lock);
++
++      return profile;
++}
++
++static inline struct aa_task_context *aa_alloc_task_context(gfp_t flags)
++{
++      struct aa_task_context *cxt;
++
++      cxt = kzalloc(sizeof(*cxt), flags);
++      if (cxt) {
++              INIT_LIST_HEAD(&cxt->list);
++              INIT_RCU_HEAD(&cxt->rcu);
++      }
++
++      return cxt;
++}
++
++static inline void aa_free_task_context(struct aa_task_context *cxt)
++{
++      if (cxt) {
++              aa_put_profile(cxt->profile);
++              aa_put_profile(cxt->previous_profile);
++              kfree(cxt);
++      }
++}
++
++/**
++ * lock_profile - lock a profile
++ * @profile: the profile to lock
++ *
++ * While the profile is locked, local interrupts are disabled. This also
++ * gives us RCU reader safety.
++ */
++static inline void lock_profile_nested(struct aa_profile *profile,
++                                     enum aa_lock_class lock_class)
++{
++      /*
++       * Lock the profile.
++       *
++       * Need to disable interrupts here because this lock is used in
++       * the task_free_security hook, which may run in RCU context.
++       */
++      if (profile)
++              spin_lock_irqsave_nested(&profile->lock, profile->int_flags,
++                                       lock_class);
++}
++
++static inline void lock_profile(struct aa_profile *profile)
++{
++      lock_profile_nested(profile, aa_lock_normal);
++}
++
++/**
++ * unlock_profile - unlock a profile
++ * @profile: the profile to unlock
++ */
++static inline void unlock_profile(struct aa_profile *profile)
++{
++      /* Unlock the profile. */
++      if (profile)
++              spin_unlock_irqrestore(&profile->lock, profile->int_flags);
++}
++
++/**
++ * lock_both_profiles  -  lock two profiles in a deadlock-free way
++ * @profile1: profile to lock (may be NULL)
++ * @profile2: profile to lock (may be NULL)
++ *
++ * The order in which profiles are passed into lock_both_profiles() /
++ * unlock_both_profiles() does not matter.
++ * While the profile is locked, local interrupts are disabled. This also
++ * gives us RCU reader safety.
++ */
++static inline void lock_both_profiles(struct aa_profile *profile1,
++                                    struct aa_profile *profile2)
++{
++      /*
++       * Lock the two profiles.
++       *
++       * We need to disable interrupts because the profile locks are
++       * used in the task_free_security hook, which may run in RCU
++       * context.
++       *
++       * Do not nest spin_lock_irqsave()/spin_unlock_irqresore():
++       * interrupts only need to be turned off once.
++       */
++      if (!profile1 || profile1 == profile2) {
++              if (profile2)
++                      spin_lock_irqsave_nested(&profile2->lock,
++                                               profile2->int_flags,
++                                               aa_lock_normal);
++      } else if (profile1 > profile2) {
++              /* profile1 cannot be NULL here. */
++              spin_lock_irqsave_nested(&profile1->lock, profile1->int_flags,
++                                       aa_lock_normal);
++              if (profile2)
++                      spin_lock_nested(&profile2->lock, aa_lock_nested);
++
++      } else {
++              /* profile2 cannot be NULL here. */
++              spin_lock_irqsave_nested(&profile2->lock, profile2->int_flags,
++                                       aa_lock_normal);
++              spin_lock_nested(&profile1->lock, aa_lock_nested);
++      }
++}
++
++/**
++ * unlock_both_profiles  -  unlock two profiles in a deadlock-free way
++ * @profile1: profile to unlock (may be NULL)
++ * @profile2: profile to unlock (may be NULL)
++ *
++ * The order in which profiles are passed into lock_both_profiles() /
++ * unlock_both_profiles() does not matter.
++ * While the profile is locked, local interrupts are disabled. This also
++ * gives us RCU reader safety.
++ */
++static inline void unlock_both_profiles(struct aa_profile *profile1,
++                                      struct aa_profile *profile2)
++{
++      /* Unlock the two profiles. */
++      if (!profile1 || profile1 == profile2) {
++              if (profile2)
++                      spin_unlock_irqrestore(&profile2->lock,
++                                             profile2->int_flags);
++      } else if (profile1 > profile2) {
++              /* profile1 cannot be NULL here. */
++              if (profile2)
++                      spin_unlock(&profile2->lock);
++              spin_unlock_irqrestore(&profile1->lock, profile1->int_flags);
++      } else {
++              /* profile2 cannot be NULL here. */
++              spin_unlock(&profile1->lock);
++              spin_unlock_irqrestore(&profile2->lock, profile2->int_flags);
++      }
++}
++
++static inline unsigned int aa_match(struct aa_dfa *dfa, const char *pathname,
++                                  int *audit_mask)
++{
++      if (dfa)
++              return aa_dfa_match(dfa, pathname, audit_mask);
++      if (audit_mask)
++              *audit_mask = 0;
++      return 0;
++}
++
++static inline int dfa_audit_mask(struct aa_dfa *dfa, unsigned int state)
++{
++      return  ACCEPT_TABLE2(dfa)[state];
++}
++
++#endif /* __INLINE_H__ */
+--- /dev/null
++++ b/security/apparmor/list.c
+@@ -0,0 +1,174 @@
++/*
++ *    Copyright (C) 1998-2007 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 <linux/seq_file.h>
++#include "apparmor.h"
++#include "inline.h"
++
++/* list of profile namespaces and lock */
++LIST_HEAD(profile_ns_list);
++rwlock_t profile_ns_list_lock = RW_LOCK_UNLOCKED;
++
++/**
++ * __aa_find_namespace  -  look up a profile namespace on the namespace list
++ * @name: name of namespace to find
++ * @head: list to search
++ *
++ * Returns a pointer to the namespace on the list, or NULL if no namespace
++ * called @name exists. The caller must hold the profile_ns_list_lock.
++ */
++struct aa_namespace *__aa_find_namespace(const char *name,
++                                     struct list_head *head)
++{
++      struct aa_namespace *ns;
++
++      list_for_each_entry(ns, head, list) {
++              if (!strcmp(ns->name, name))
++                      return ns;
++      }
++
++      return NULL;
++}
++
++/**
++ * __aa_find_profile  -  look up a profile on the profile list
++ * @name: name of profile to find
++ * @head: list to search
++ *
++ * Returns a pointer to the profile on the list, or NULL if no profile
++ * called @name exists. The caller must hold the profile_list_lock.
++ */
++struct aa_profile *__aa_find_profile(const char *name, struct list_head *head)
++{
++      struct aa_profile *profile;
++
++      list_for_each_entry(profile, head, list) {
++              if (!strcmp(profile->name, name))
++                      return profile;
++      }
++
++      return NULL;
++}
++
++static void aa_profile_list_release(struct list_head *head)
++{
++      struct aa_profile *profile, *tmp;
++      list_for_each_entry_safe(profile, tmp, head, list) {
++              /* Remove the profile from each task context it is on. */
++              lock_profile(profile);
++              profile->isstale = 1;
++              aa_unconfine_tasks(profile);
++              list_del_init(&profile->list);
++              unlock_profile(profile);
++              aa_put_profile(profile);
++      }
++}
++
++/**
++ * aa_profilelist_release - Remove all profiles from profile_list
++ */
++void aa_profile_ns_list_release(void)
++{
++      struct aa_namespace *ns, *tmp;
++
++      /* Remove and release all the profiles on namespace profile lists. */
++      write_lock(&profile_ns_list_lock);
++      list_for_each_entry_safe(ns, tmp, &profile_ns_list, list) {
++              write_lock(&ns->lock);
++              aa_profile_list_release(&ns->profiles);
++              list_del_init(&ns->list);
++              write_unlock(&ns->lock);
++              aa_put_namespace(ns);
++      }
++      write_unlock(&profile_ns_list_lock);
++}
++
++
++static struct aa_profile *next_profile(struct aa_profile *profile)
++{
++      struct aa_profile *next = profile;
++      struct aa_namespace *ns;
++
++      list_for_each_entry_continue(next, &profile->ns->profiles, list)
++              return next;
++
++      ns = profile->ns;
++      read_unlock(&ns->lock);
++      list_for_each_entry_continue(ns, &profile_ns_list, list) {
++              read_lock(&ns->lock);
++              list_for_each_entry(profile, &ns->profiles, list)
++                      return profile;
++              read_unlock(&ns->lock);
++      }
++      return NULL;
++}
++
++static void *p_start(struct seq_file *f, loff_t *pos)
++{
++      struct aa_namespace *ns;
++      loff_t l = *pos;
++
++      read_lock(&profile_ns_list_lock);
++      if (!list_empty(&profile_ns_list)) {
++              struct aa_profile *profile = NULL;
++              ns = list_first_entry(&profile_ns_list, typeof(*ns), list);
++              read_lock(&ns->lock);
++              if (!list_empty(&ns->profiles))
++                      profile = list_first_entry(&ns->profiles,
++                                                 typeof(*profile), list);
++              else
++                      read_unlock(&ns->lock);
++              for ( ; profile && l > 0; l--)
++                      profile = next_profile(profile);
++              return profile;
++      }
++      return NULL;
++}
++
++static void *p_next(struct seq_file *f, void *p, loff_t *pos)
++{
++      struct aa_profile *profile = (struct aa_profile *) p;
++
++      (*pos)++;
++      profile = next_profile(profile);
++
++      return profile;
++}
++
++static void p_stop(struct seq_file *f, void *p)
++{
++      struct aa_profile *profile = (struct aa_profile *) p;
++
++      if (profile)
++              read_unlock(&profile->ns->lock);
++      read_unlock(&profile_ns_list_lock);
++}
++
++static int seq_show_profile(struct seq_file *f, void *p)
++{
++      struct aa_profile *profile = (struct aa_profile *)p;
++
++      if (profile->ns == default_namespace)
++          seq_printf(f, "%s (%s)\n", profile->name,
++                     PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
++      else
++          seq_printf(f, ":%s:%s (%s)\n", profile->ns->name, profile->name,
++                     PROFILE_COMPLAIN(profile) ? "complain" : "enforce");
++      return 0;
++}
++
++/* Used in apparmorfs.c */
++struct seq_operations apparmorfs_profiles_op = {
++      .start =        p_start,
++      .next =         p_next,
++      .stop =         p_stop,
++      .show =         seq_show_profile,
++};
+--- /dev/null
++++ b/security/apparmor/locking.txt
+@@ -0,0 +1,68 @@
++Locking in AppArmor
++===================
++
++Lock hierarchy:
++
++      aa_interface_lock
++        profile_list_lock
++          aa_profile->lock
++            task_lock()
++
++
++Which lock protects what?
++
++      /-----------------------+-------------------------------\
++      | Variable              | Lock                          |
++      >-----------------------+-------------------------------<
++      | profile_list          | profile_list_lock             |
++      +-----------------------+-------------------------------+
++      | aa_profile            | (reference count)             |
++      +-----------------------+-------------------------------+
++      | aa_profile->          | aa_profile->lock              |
++      |   isstale,            |                               |
++      |   task_contexts       |                               |
++      +-----------------------+-------------------------------+
++      | task_struct->security | read: RCU                     |
++      |                       | write: task_lock()            |
++      +-----------------------+-------------------------------+
++      | aa_profile->sub       | handle on the profile (list   |
++      |                       | is never modified)            |
++      \-----------------------+-------------------------------/
++
++(Obviously, the list_heads embedded in data structures are always
++protected with the lock that also protects the list.)
++
++When moving a task context from one profile to another, we grab both
++profile locks with lock_both_profiles(). This ensures that both locks
++are always taken in the same order, and so we won't deadlock.
++
++Since task_struct->security is RCU protected the aa_task_struct it
++references is only guarenteed to exist for the rcu cycle.  Where
++aa_task_context->profile is needed in blocking operations the
++profile's reference count is incremented and the profile reference
++is used.
++
++Profiles on profile_list are never stale: when a profile becomes stale,
++it is removed from profile_list at the same time (under profile_list_lock
++and aa_profile->lock).
++
++The aa_interface_lock is taken whenever user-space modifies the profile
++list, and can sleep. This ensures that profile loading/replacement/removal
++won't race with itself. We release the profile_list_lock as soon as
++possible to avoid stalling exec during profile loading/replacement/removal.
++
++AppArmor uses lock subtyping to avoid false positives from lockdep.  The
++profile lock is often taken nested, but it is guaranteed to be in a lock
++safe order and not the same lock when done, so it is safe.
++
++A third lock type (aa_lock_task_release) is given to the profile lock
++when it is taken in soft irq context during task release (aa_release).
++This is to avoid a false positive between the task lock and the profile
++lock.  In task context the profile lock wraps the task lock with irqs
++off, but the kernel takes the task lock with irqs enabled.  This won't
++result in a deadlock because for a deadlock to occur the kernel must
++take dead task A's lock (irqs on), the rcu callback hook freeing
++dead task A must be run and AppArmor must be changing the profile on
++dead task A.  The kernel should not be taking a dead task's task_lock
++at the same time the task is being freed by task rcu cleanup other wise
++the task would not be out of its quiescent period.
+--- /dev/null
++++ b/security/apparmor/procattr.c
+@@ -0,0 +1,195 @@
++/*
++ *    Copyright (C) 1998-2007 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
++ */
++
++#include "apparmor.h"
++#include "inline.h"
++
++int aa_getprocattr(struct aa_profile *profile, char **string, unsigned *len)
++{
++      char *str;
++
++      if (profile) {
++              const char *mode_str = PROFILE_COMPLAIN(profile) ?
++                      " (complain)" : " (enforce)";
++              int mode_len, name_len, ns_len = 0;
++
++              mode_len = strlen(mode_str);
++              name_len = strlen(profile->name);
++              if (profile->ns != default_namespace)
++                      ns_len = strlen(profile->ns->name) + 2;
++              *len = mode_len + ns_len + name_len + 1;
++              str = kmalloc(*len, GFP_ATOMIC);
++              if (!str)
++                      return -ENOMEM;
++
++              if (ns_len) {
++                      *str++ = ':';
++                      memcpy(str, profile->ns->name, ns_len - 2);
++                      str += ns_len - 2;
++                      *str++ = ':';
++              }
++              memcpy(str, profile->name, name_len);
++              str += name_len;
++              memcpy(str, mode_str, mode_len);
++              str += mode_len;
++              *str++ = '\n';
++              str -= *len;
++      } else {
++              const char *unconfined_str = "unconfined\n";
++
++              *len = strlen(unconfined_str);
++              str = kmalloc(*len, GFP_ATOMIC);
++              if (!str)
++                      return -ENOMEM;
++
++              memcpy(str, unconfined_str, *len);
++      }
++      *string = str;
++
++      return 0;
++}
++
++static char *split_token_from_name(const char *op, char *args, u64 *cookie)
++{
++      char *name;
++
++      *cookie = simple_strtoull(args, &name, 16);
++      if ((name == args) || *name != '^') {
++              AA_ERROR("%s: Invalid input '%s'", op, args);
++              return ERR_PTR(-EINVAL);
++      }
++
++      name++;  /* skip ^ */
++      if (!*name)
++              name = NULL;
++      return name;
++}
++
++int aa_setprocattr_changehat(char *args)
++{
++      char *hat;
++      u64 cookie;
++
++      hat = split_token_from_name("change_hat", args, &cookie);
++      if (IS_ERR(hat))
++              return PTR_ERR(hat);
++
++      if (!hat && !cookie) {
++              AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic");
++              return -EINVAL;
++      }
++
++      AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n",
++               __FUNCTION__, cookie, hat ? hat : NULL);
++
++      return aa_change_hat(hat, cookie);
++}
++
++int aa_setprocattr_changeprofile(char *args)
++{
++      char *name = args, *ns_name = NULL;
++
++      if (name[0] == ':') {
++              char *split = strchr(&name[1], ':');
++              if (split) {
++                      *split = 0;
++                      ns_name = &name[1];
++                      name = split + 1;
++              }
++      }
++
++      return aa_change_profile(ns_name, name);
++}
++
++int aa_setprocattr_setprofile(struct task_struct *task, char *args)
++{
++      struct aa_profile *old_profile, *new_profile;
++      struct aa_namespace *ns;
++      struct aa_audit sa;
++      char *name, *ns_name = NULL;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "profile_set";
++      sa.gfp_mask = GFP_KERNEL;
++      sa.task = task->pid;
++
++      AA_DEBUG("%s: current %d\n",
++               __FUNCTION__, current->pid);
++
++      name = args;
++      if (args[0] != '/') {
++              char *split = strchr(args, ':');
++              if (split) {
++                      *split = 0;
++                      ns_name = args;
++                      name = split + 1;
++              }
++      }
++      if (ns_name)
++              ns = aa_find_namespace(ns_name);
++      else
++              ns = aa_get_namespace(default_namespace);
++      if (!ns) {
++              sa.name = ns_name;
++              sa.info = "unknown namespace";
++              aa_audit_reject(NULL, &sa);
++              aa_put_namespace(ns);
++              return -EINVAL;
++      }
++
++repeat:
++      if (strcmp(name, "unconfined") == 0)
++              new_profile = NULL;
++      else {
++              new_profile = aa_find_profile(ns, name);
++              if (!new_profile) {
++                      sa.name = ns_name;
++                      sa.name2 = name;
++                      sa.info = "unknown profile";
++                      aa_audit_reject(NULL, &sa);
++                      aa_put_namespace(ns);
++                      return -EINVAL;
++              }
++      }
++
++      old_profile = __aa_replace_profile(task, new_profile);
++      if (IS_ERR(old_profile)) {
++              int error;
++
++              aa_put_profile(new_profile);
++              error = PTR_ERR(old_profile);
++              if (error == -ESTALE)
++                      goto repeat;
++              aa_put_namespace(ns);
++              return error;
++      }
++
++      if (new_profile) {
++              sa.name = ns_name;
++              sa.name2 = name;
++              sa.name3 = old_profile ? old_profile->name :
++                      "unconfined";
++              aa_audit_status(NULL, &sa);
++      } else {
++              if (old_profile) {
++                      sa.name = "unconfined";
++                      sa.name2 = old_profile->name;
++                      aa_audit_status(NULL, &sa);
++              } else {
++                      sa.info = "task is unconfined";
++                      aa_audit_status(NULL, &sa);
++              }
++      }
++      aa_put_namespace(ns);
++      aa_put_profile(old_profile);
++      aa_put_profile(new_profile);
++      return 0;
++}
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-module_interface.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-module_interface.diff
new file mode 100644 (file)
index 0000000..f373428
--- /dev/null
@@ -0,0 +1,1350 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: Profile loading and manipulation, pathname matching
+
+Pathname matching, transition table loading, profile loading and
+manipulation.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+
+---
+ security/apparmor/match.c            |  364 ++++++++++++++
+ security/apparmor/match.h            |   87 +++
+ security/apparmor/module_interface.c |  875 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 1326 insertions(+)
+
+--- /dev/null
++++ b/security/apparmor/match.c
+@@ -0,0 +1,364 @@
++/*
++ *    Copyright (C) 2007 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.
++ *
++ *    Regular expression transition table matching
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include "apparmor.h"
++#include "match.h"
++#include "inline.h"
++
++static struct table_header *unpack_table(void *blob, size_t bsize)
++{
++      struct table_header *table = NULL;
++      struct table_header th;
++      size_t tsize;
++
++      if (bsize < sizeof(struct table_header))
++              goto out;
++
++      th.td_id = be16_to_cpu(*(u16 *) (blob));
++      th.td_flags = be16_to_cpu(*(u16 *) (blob + 2));
++      th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8));
++      blob += sizeof(struct table_header);
++
++      if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 ||
++              th.td_flags == YYTD_DATA8))
++              goto out;
++
++      tsize = table_size(th.td_lolen, th.td_flags);
++      if (bsize < tsize)
++              goto out;
++
++      table = kmalloc(tsize, GFP_KERNEL);
++      if (table) {
++              *table = th;
++              if (th.td_flags == YYTD_DATA8)
++                      UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
++                                   u8, byte_to_byte);
++              else if (th.td_flags == YYTD_DATA16)
++                      UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
++                                   u16, be16_to_cpu);
++              else
++                      UNPACK_ARRAY(table->td_data, blob, th.td_lolen,
++                                   u32, be32_to_cpu);
++      }
++
++out:
++      return table;
++}
++
++int unpack_dfa(struct aa_dfa *dfa, void *blob, size_t size)
++{
++      int hsize, i;
++      int error = -ENOMEM;
++
++      /* get dfa table set header */
++      if (size < sizeof(struct table_set_header))
++              goto fail;
++
++      if (ntohl(*(u32 *)blob) != YYTH_MAGIC)
++              goto fail;
++
++      hsize = ntohl(*(u32 *)(blob + 4));
++      if (size < hsize)
++              goto fail;
++
++      blob += hsize;
++      size -= hsize;
++
++      error = -EPROTO;
++      while (size > 0) {
++              struct table_header *table;
++              table = unpack_table(blob, size);
++              if (!table)
++                      goto fail;
++
++              switch(table->td_id) {
++              case YYTD_ID_ACCEPT:
++              case YYTD_ID_ACCEPT2:
++              case YYTD_ID_BASE:
++                      dfa->tables[table->td_id - 1] = table;
++                      if (table->td_flags != YYTD_DATA32)
++                              goto fail;
++                      break;
++              case YYTD_ID_DEF:
++              case YYTD_ID_NXT:
++              case YYTD_ID_CHK:
++                      dfa->tables[table->td_id - 1] = table;
++                      if (table->td_flags != YYTD_DATA16)
++                              goto fail;
++                      break;
++              case YYTD_ID_EC:
++                      dfa->tables[table->td_id - 1] = table;
++                      if (table->td_flags != YYTD_DATA8)
++                              goto fail;
++                      break;
++              default:
++                      kfree(table);
++                      goto fail;
++              }
++
++              blob += table_size(table->td_lolen, table->td_flags);
++              size -= table_size(table->td_lolen, table->td_flags);
++      }
++
++      return 0;
++
++fail:
++      for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) {
++              if (dfa->tables[i]) {
++                      kfree(dfa->tables[i]);
++                      dfa->tables[i] = NULL;
++              }
++      }
++      return error;
++}
++
++/**
++ * verify_dfa - verify that all the transitions and states in the dfa tables
++ *              are in bounds.
++ * @dfa: dfa to test
++ *
++ * assumes dfa has gone through the verification done by unpacking
++ */
++int verify_dfa(struct aa_dfa *dfa)
++{
++      size_t i, state_count, trans_count;
++      int error = -EPROTO;
++
++      /* check that required tables exist */
++      if (!(dfa->tables[YYTD_ID_ACCEPT - 1] &&
++            dfa->tables[YYTD_ID_ACCEPT2 - 1] &&
++            dfa->tables[YYTD_ID_DEF - 1] &&
++            dfa->tables[YYTD_ID_BASE - 1] &&
++            dfa->tables[YYTD_ID_NXT - 1] &&
++            dfa->tables[YYTD_ID_CHK - 1]))
++              goto out;
++
++      /* accept.size == default.size == base.size */
++      state_count = dfa->tables[YYTD_ID_BASE - 1]->td_lolen;
++      if (!(state_count == dfa->tables[YYTD_ID_DEF - 1]->td_lolen &&
++            state_count == dfa->tables[YYTD_ID_ACCEPT - 1]->td_lolen &&
++            state_count == dfa->tables[YYTD_ID_ACCEPT2 - 1]->td_lolen))
++              goto out;
++
++      /* next.size == chk.size */
++      trans_count = dfa->tables[YYTD_ID_NXT - 1]->td_lolen;
++      if (trans_count != dfa->tables[YYTD_ID_CHK - 1]->td_lolen)
++              goto out;
++
++      /* if equivalence classes then its table size must be 256 */
++      if (dfa->tables[YYTD_ID_EC - 1] &&
++          dfa->tables[YYTD_ID_EC - 1]->td_lolen != 256)
++              goto out;
++
++      for (i = 0; i < state_count; i++) {
++              if (DEFAULT_TABLE(dfa)[i] >= state_count)
++                      goto out;
++              if (BASE_TABLE(dfa)[i] >= trans_count + 256)
++                      goto out;
++      }
++
++      for (i = 0; i < trans_count ; i++) {
++              if (NEXT_TABLE(dfa)[i] >= state_count)
++                      goto out;
++              if (CHECK_TABLE(dfa)[i] >= state_count)
++                      goto out;
++      }
++
++      /* verify accept permissions */
++      for (i = 0; i < state_count; i++) {
++              int mode = ACCEPT_TABLE(dfa)[i];
++
++              if (mode & ~AA_VALID_PERM_MASK)
++                      goto out;
++              if (ACCEPT_TABLE2(dfa)[i] & ~AA_VALID_PERM2_MASK)
++                      goto out;
++
++              /* if any exec modifier is set MAY_EXEC must be set */
++              if ((mode & AA_USER_EXEC_TYPE) && !(mode & AA_USER_EXEC))
++                      goto out;
++              if ((mode & AA_OTHER_EXEC_TYPE) && !(mode & AA_OTHER_EXEC))
++                      goto out;
++      }
++
++      error = 0;
++out:
++      return error;
++}
++
++struct aa_dfa *aa_match_alloc(void)
++{
++      return kzalloc(sizeof(struct aa_dfa), GFP_KERNEL);
++}
++
++void aa_match_free(struct aa_dfa *dfa)
++{
++      if (dfa) {
++              int i;
++
++              for (i = 0; i < ARRAY_SIZE(dfa->tables); i++)
++                      kfree(dfa->tables[i]);
++      }
++      kfree(dfa);
++}
++
++/**
++ * aa_dfa_next_state_len - traverse @dfa to find state @str stops at
++ * @dfa: the dfa to match @str against
++ * @start: the state of the dfa to start matching in
++ * @str: the string of bytes to match against the dfa
++ * @len: length of the string of bytes to match
++ *
++ * aa_dfa_next_state will match @str against the dfa and return the state it
++ * finished matching in. The final state can be used to look up the accepting
++ * label, or as the start state of a continuing match.
++ *
++ * aa_dfa_next_state could be implement using this function by doing
++ * return aa_dfa_next_state_len(dfa, start, str, strlen(str));
++ * but that would require traversing the string twice and be slightly
++ * slower.
++ */
++unsigned int aa_dfa_next_state_len(struct aa_dfa *dfa, unsigned int start,
++                                 const char *str, int len)
++{
++      u16 *def = DEFAULT_TABLE(dfa);
++      u32 *base = BASE_TABLE(dfa);
++      u16 *next = NEXT_TABLE(dfa);
++      u16 *check = CHECK_TABLE(dfa);
++      unsigned int state = start, pos;
++
++      if (state == 0)
++              return 0;
++
++      /* current state is <state>, matching character *str */
++      if (dfa->tables[YYTD_ID_EC - 1]) {
++              u8 *equiv = EQUIV_TABLE(dfa);
++              for (; len; len--) {
++                      pos = base[state] + equiv[(u8)*str++];
++                      if (check[pos] == state)
++                              state = next[pos];
++                      else
++                              state = def[state];
++              }
++      } else {
++              for (; len; len--) {
++                      pos = base[state] + (u8)*str++;
++                      if (check[pos] == state)
++                              state = next[pos];
++                      else
++                              state = def[state];
++              }
++      }
++      return state;
++}
++
++/**
++ * aa_dfa_next_state - traverse @dfa to find state @str stops at
++ * @dfa: the dfa to match @str against
++ * @start: the state of the dfa to start matching in
++ * @str: the null terminated string of bytes to match against the dfa
++ *
++ * aa_dfa_next_state will match @str against the dfa and return the state it
++ * finished matching in. The final state can be used to look up the accepting
++ * label, or as the start state of a continuing match.
++ */
++unsigned int aa_dfa_next_state(struct aa_dfa *dfa, unsigned int start,
++                             const char *str)
++{
++      u16 *def = DEFAULT_TABLE(dfa);
++      u32 *base = BASE_TABLE(dfa);
++      u16 *next = NEXT_TABLE(dfa);
++      u16 *check = CHECK_TABLE(dfa);
++      unsigned int state = start, pos;
++
++      if (state == 0)
++              return 0;
++
++      /* current state is <state>, matching character *str */
++      if (dfa->tables[YYTD_ID_EC - 1]) {
++              u8 *equiv = EQUIV_TABLE(dfa);
++              while (*str) {
++                      pos = base[state] + equiv[(u8)*str++];
++                      if (check[pos] == state)
++                              state = next[pos];
++                      else
++                              state = def[state];
++              }
++      } else {
++              while (*str) {
++                      pos = base[state] + (u8)*str++;
++                      if (check[pos] == state)
++                              state = next[pos];
++                      else
++                              state = def[state];
++              }
++      }
++      return state;
++}
++
++/**
++ * aa_dfa_null_transition - step to next state after null character
++ * @dfa: the dfa to match against
++ * @start: the state of the dfa to start matching in
++ *
++ * aa_dfa_null_transition transitions to the next state after a null
++ * character which is not used in standard matching and is only
++ * used to seperate pairs.
++ */
++unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, unsigned int start)
++{
++      return aa_dfa_next_state_len(dfa, start, "", 1);
++}
++
++/**
++ * aa_dfa_match - find accept perm for @str in @dfa
++ * @dfa: the dfa to match @str against
++ * @str: the string to match against the dfa
++ * @audit_mask: the audit_mask for the final state
++ *
++ * aa_dfa_match will match @str and return the accept perms for the
++ * final state.
++ */
++unsigned int aa_dfa_match(struct aa_dfa *dfa, const char *str, int *audit_mask)
++{
++      int state = aa_dfa_next_state(dfa, DFA_START, str);
++      if (audit_mask)
++              *audit_mask = dfa_audit_mask(dfa, state);
++      return ACCEPT_TABLE(dfa)[state];
++}
++
++/**
++ * aa_match_state - find accept perm and state for @str in @dfa
++ * @dfa: the dfa to match @str against
++ * @start: the state to start the match from
++ * @str: the string to match against the dfa
++ * @final: the state that the match finished in
++ *
++ * aa_match_state will match @str and return the accept perms, and @final
++ * state, the match occured in.
++ */
++unsigned int aa_match_state(struct aa_dfa *dfa, unsigned int start,
++                          const char *str, unsigned int *final)
++{
++      unsigned int state;
++      if (dfa) {
++              state = aa_dfa_next_state(dfa, start, str);
++              if (final)
++                      *final = state;
++              return ACCEPT_TABLE(dfa)[state];
++      }
++      if (final)
++              *final = 0;
++      return 0;
++}
++
+--- /dev/null
++++ b/security/apparmor/match.h
+@@ -0,0 +1,87 @@
++/*
++ *    Copyright (C) 2007 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
++
++#define DFA_START                     1
++
++/**
++ * The format used for transition tables is based on the GNU flex table
++ * file format (--tables-file option; see Table File Format in the flex
++ * info pages and the flex sources for documentation). The magic number
++ * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because
++ * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used
++ * slightly differently (see the apparmor-parser package).
++ */
++
++#define YYTH_MAGIC    0x1B5E783D
++
++struct table_set_header {
++      u32             th_magic;       /* YYTH_MAGIC */
++      u32             th_hsize;
++      u32             th_ssize;
++      u16             th_flags;
++      char            th_version[];
++};
++
++#define       YYTD_ID_ACCEPT  1
++#define YYTD_ID_BASE  2
++#define YYTD_ID_CHK   3
++#define YYTD_ID_DEF   4
++#define YYTD_ID_EC    5
++#define YYTD_ID_META  6
++#define YYTD_ID_ACCEPT2 7
++#define YYTD_ID_NXT   8
++
++
++#define YYTD_DATA8    1
++#define YYTD_DATA16   2
++#define YYTD_DATA32   4
++
++struct table_header {
++      u16             td_id;
++      u16             td_flags;
++      u32             td_hilen;
++      u32             td_lolen;
++      char            td_data[];
++};
++
++#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF - 1]->td_data))
++#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE - 1]->td_data))
++#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT - 1]->td_data))
++#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK - 1]->td_data))
++#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC - 1]->td_data))
++#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT - 1]->td_data))
++#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2 -1]->td_data))
++
++struct aa_dfa {
++      struct table_header *tables[YYTD_ID_NXT];
++};
++
++#define byte_to_byte(X) (X)
++
++#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \
++      do { \
++              typeof(LEN) __i; \
++              TYPE *__t = (TYPE *) TABLE; \
++              TYPE *__b = (TYPE *) BLOB; \
++              for (__i = 0; __i < LEN; __i++) { \
++                      __t[__i] = NTOHX(__b[__i]); \
++              } \
++      } while (0)
++
++static inline size_t table_size(size_t len, size_t el_size)
++{
++      return ALIGN(sizeof(struct table_header) + len * el_size, 8);
++}
++
++#endif /* __MATCH_H */
+--- /dev/null
++++ b/security/apparmor/module_interface.c
+@@ -0,0 +1,875 @@
++/*
++ *    Copyright (C) 1998-2007 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 <asm/unaligned.h>
++
++#include "apparmor.h"
++#include "inline.h"
++
++/*
++ * This mutex is used to synchronize profile adds, replacements, and
++ * removals: we only allow one of these operations at a time.
++ * We do not use the profile list lock here in order to avoid blocking
++ * exec during those operations.  (Exec involves a profile list lookup
++ * for named-profile transitions.)
++ */
++DEFINE_MUTEX(aa_interface_lock);
++
++/*
++ * The AppArmor interface treats data as a type byte followed by the
++ * actual data.  The interface has the notion of a a named entry
++ * which has a name (AA_NAME typecode followed by name string) followed by
++ * the entries typecode and data.  Named types allow for optional
++ * elements and extensions to be added and tested for without breaking
++ * backwards compatability.
++ */
++
++enum aa_code {
++      AA_U8,
++      AA_U16,
++      AA_U32,
++      AA_U64,
++      AA_NAME,        /* same as string except it is items name */
++      AA_STRING,
++      AA_BLOB,
++      AA_STRUCT,
++      AA_STRUCTEND,
++      AA_LIST,
++      AA_LISTEND,
++      AA_ARRAY,
++      AA_ARRAYEND,
++};
++
++/*
++ * aa_ext is the read of the buffer containing the serialized profile.  The
++ * data is copied into a kernel buffer in apparmorfs and then handed off to
++ * the unpack routines.
++ */
++struct aa_ext {
++      void *start;
++      void *end;
++      void *pos;      /* pointer to current position in the buffer */
++      u32 version;
++      char *ns_name;
++};
++
++static inline int aa_inbounds(struct aa_ext *e, size_t size)
++{
++      return (size <= e->end - e->pos);
++}
++
++/**
++ * aa_u16_chunck - test and do bounds checking for a u16 size based chunk
++ * @e: serialized data read head
++ * @chunk: start address for chunk of data
++ *
++ * return the size of chunk found with the read head at the end of
++ * the chunk.
++ */
++static size_t aa_is_u16_chunk(struct aa_ext *e, char **chunk)
++{
++      void *pos = e->pos;
++      size_t size = 0;
++
++      if (!aa_inbounds(e, sizeof(u16)))
++              goto fail;
++      size = le16_to_cpu(get_unaligned((u16 *)e->pos));
++      e->pos += sizeof(u16);
++      if (!aa_inbounds(e, size))
++              goto fail;
++      *chunk = e->pos;
++      e->pos += size;
++      return size;
++
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++static inline int aa_is_X(struct aa_ext *e, enum aa_code code)
++{
++      if (!aa_inbounds(e, 1))
++              return 0;
++      if (*(u8 *) e->pos != code)
++              return 0;
++      e->pos++;
++      return 1;
++}
++
++/**
++ * 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
++ * @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 @name is specified then there must be a matching
++ * name element in the stream.  If @name is NULL any name element will be
++ * skipped and only the typecode will be tested.
++ * returns 1 on success (both type code and name tests match) and the read
++ * head is advanced past the headers
++ * returns %0 if either match failes, the read head does not move
++ */
++static int aa_is_nameX(struct aa_ext *e, enum aa_code code, const char *name)
++{
++      void *pos = e->pos;
++      /*
++       * 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)) {
++              char *tag;
++              size_t size = aa_is_u16_chunk(e, &tag);
++              /* if a name is specified it must match. otherwise skip tag */
++              if (name && (!size || strcmp(name, tag)))
++                      goto fail;
++      } else if (name) {
++              /* if a name is specified and there is no name tag fail */
++              goto fail;
++      }
++
++      /* now check if type code matches */
++      if (aa_is_X(e, code))
++              return 1;
++
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++static int aa_is_u16(struct aa_ext *e, u16 *data, const char *name)
++{
++      void *pos = e->pos;
++      if (aa_is_nameX(e, AA_U16, name)) {
++              if (!aa_inbounds(e, sizeof(u16)))
++                      goto fail;
++              if (data)
++                      *data = le16_to_cpu(get_unaligned((u16 *)e->pos));
++              e->pos += sizeof(u16);
++              return 1;
++      }
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++static int aa_is_u32(struct aa_ext *e, u32 *data, const char *name)
++{
++      void *pos = e->pos;
++      if (aa_is_nameX(e, AA_U32, name)) {
++              if (!aa_inbounds(e, sizeof(u32)))
++                      goto fail;
++              if (data)
++                      *data = le32_to_cpu(get_unaligned((u32 *)e->pos));
++              e->pos += sizeof(u32);
++              return 1;
++      }
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++static size_t aa_is_array(struct aa_ext *e, const char *name)
++{
++      void *pos = e->pos;
++      if (aa_is_nameX(e, AA_ARRAY, name)) {
++              int size;
++              if (!aa_inbounds(e, sizeof(u16)))
++                      goto fail;
++              size = (int) le16_to_cpu(get_unaligned((u16 *)e->pos));
++              e->pos += sizeof(u16);
++              return size;
++      }
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++static size_t aa_is_blob(struct aa_ext *e, char **blob, const char *name)
++{
++      void *pos = e->pos;
++      if (aa_is_nameX(e, AA_BLOB, name)) {
++              u32 size;
++              if (!aa_inbounds(e, sizeof(u32)))
++                      goto fail;
++              size = le32_to_cpu(get_unaligned((u32 *)e->pos));
++              e->pos += sizeof(u32);
++              if (aa_inbounds(e, (size_t) size)) {
++                      * blob = e->pos;
++                      e->pos += size;
++                      return size;
++              }
++      }
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++static int aa_is_dynstring(struct aa_ext *e, char **string, const char *name)
++{
++      char *src_str;
++      size_t size = 0;
++      void *pos = e->pos;
++      *string = NULL;
++      if (aa_is_nameX(e, AA_STRING, name) &&
++          (size = aa_is_u16_chunk(e, &src_str))) {
++              char *str;
++              if (!(str = kmalloc(size, GFP_KERNEL)))
++                      goto fail;
++              memcpy(str, src_str, size);
++              *string = str;
++      }
++
++      return size;
++
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++/**
++ * aa_unpack_dfa - unpack a file rule dfa
++ * @e: serialized data extent information
++ *
++ * returns dfa or ERR_PTR
++ */
++static struct aa_dfa *aa_unpack_dfa(struct aa_ext *e)
++{
++      char *blob = NULL;
++      size_t size, error = 0;
++      struct aa_dfa *dfa = NULL;
++
++      size = aa_is_blob(e, &blob, "aadfa");
++      if (size) {
++              dfa = aa_match_alloc();
++              if (dfa) {
++                      /*
++                       * The dfa is aligned with in the blob to 8 bytes
++                       * from the beginning of the stream.
++                       */
++                      size_t sz = blob - (char *) e->start;
++                      size_t pad = ALIGN(sz, 8) - sz;
++                      error = unpack_dfa(dfa, blob + pad, size - pad);
++                      if (!error)
++                              error = verify_dfa(dfa);
++              } else {
++                      error = -ENOMEM;
++              }
++
++              if (error) {
++                      aa_match_free(dfa);
++                      dfa = ERR_PTR(error);
++              }
++      }
++
++      return dfa;
++}
++
++static int aa_unpack_exec_table(struct aa_ext *e, struct aa_profile *profile)
++{
++      void *pos = e->pos;
++
++      /* exec table is optional */
++      if (aa_is_nameX(e, AA_STRUCT, "xtable")) {
++              int i, size;
++
++              size = aa_is_array(e, NULL);
++              /* currently 4 exec bits and entries 0-3 are reserved iupcx */
++              if (size > 16 - 4)
++                      goto fail;
++              profile->exec_table = kzalloc(sizeof(char *) * size,
++                                            GFP_KERNEL);
++              if (!profile->exec_table)
++                      goto fail;
++
++              for (i = 0; i < size; i++) {
++                  char *tmp;
++                      if (!aa_is_dynstring(e, &tmp, NULL))
++                              goto fail;
++                      /* note: strings beginning with a : have an embedded
++                         \0 seperating the profile ns name from the profile
++                         name */
++                      profile->exec_table[i] = tmp;
++              }
++              if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
++                      goto fail;
++              if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
++                      goto fail;
++              profile->exec_table_size = size;
++      }
++      return 1;
++
++fail:
++      e->pos = pos;
++      return 0;
++}
++
++/**
++ * aa_unpack_profile - unpack a serialized profile
++ * @e: serialized data extent information
++ * @sa: audit struct for the operation
++ */
++static struct aa_profile *aa_unpack_profile(struct aa_ext *e,
++                                          struct aa_audit *sa)
++{
++      struct aa_profile *profile = NULL;
++
++      int error = -EPROTO;
++
++      profile = alloc_aa_profile();
++      if (!profile)
++              return ERR_PTR(-ENOMEM);
++
++      /* check that we have the right struct being passed */
++      if (!aa_is_nameX(e, AA_STRUCT, "profile"))
++              goto fail;
++      if (!aa_is_dynstring(e, &profile->name, NULL))
++              goto fail;
++
++      /* per profile debug flags (complain, audit) */
++      if (!aa_is_nameX(e, AA_STRUCT, "flags"))
++              goto fail;
++      if (!aa_is_u32(e, &(profile->flags.hat), NULL))
++              goto fail;
++      if (!aa_is_u32(e, &(profile->flags.complain), NULL))
++              goto fail;
++      if (!aa_is_u32(e, &(profile->flags.audit), NULL))
++              goto fail;
++      if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
++              goto fail;
++
++      if (!aa_is_u32(e, &(profile->capabilities), NULL))
++              goto fail;
++      if (!aa_is_u32(e, &(profile->audit_caps), NULL))
++              goto fail;
++      if (!aa_is_u32(e, &(profile->quiet_caps), NULL))
++              goto fail;
++      if (!aa_is_u32(e, &(profile->set_caps), NULL))
++              goto fail;
++
++      /* get file rules */
++      profile->file_rules = aa_unpack_dfa(e);
++      if (IS_ERR(profile->file_rules)) {
++              error = PTR_ERR(profile->file_rules);
++              profile->file_rules = NULL;
++              goto fail;
++      }
++
++      if (!aa_unpack_exec_table(e, profile))
++              goto fail;
++
++      if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
++              goto fail;
++
++      return profile;
++
++fail:
++      sa->name = profile && profile->name ? profile->name : "unknown";
++      if (!sa->info)
++              sa->info = "failed to unpack profile";
++      aa_audit_status(NULL, sa);
++
++      if (profile)
++              free_aa_profile(profile);
++
++      return ERR_PTR(error);
++}
++
++/**
++ * aa_verify_head - unpack serialized stream header
++ * @e: serialized data read head
++ * @operation: operation header is being verified for
++ *
++ * returns error or 0 if header is good
++ */
++static int aa_verify_header(struct aa_ext *e, struct aa_audit *sa)
++{
++      /* get the interface version */
++      if (!aa_is_u32(e, &e->version, "version")) {
++              sa->info = "invalid profile format";
++              aa_audit_status(NULL, sa);
++              return -EPROTONOSUPPORT;
++      }
++
++      /* check that the interface version is currently supported */
++      if (e->version != 5) {
++              sa->info = "unsupported interface version";
++              aa_audit_status(NULL, sa);
++              return -EPROTONOSUPPORT;
++      }
++
++      /* read the namespace if present */
++      if (!aa_is_dynstring(e, &e->ns_name, "namespace")) {
++              e->ns_name = NULL;
++      }
++
++      return 0;
++}
++
++/**
++ * aa_add_profile - Unpack and add a new profile to the profile list
++ * @data: serialized data stream
++ * @size: size of the serialized data stream
++ */
++ssize_t aa_add_profile(void *data, size_t size)
++{
++      struct aa_profile *profile = NULL;
++      struct aa_namespace *ns = NULL;
++      struct aa_ext e = {
++              .start = data,
++              .end = data + size,
++              .pos = data,
++              .ns_name = NULL
++      };
++      ssize_t error;
++      struct aa_audit sa;
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "profile_load";
++      sa.gfp_mask = GFP_KERNEL;
++
++      error = aa_verify_header(&e, &sa);
++      if (error)
++              return error;
++
++      profile = aa_unpack_profile(&e, &sa);
++      if (IS_ERR(profile))
++              return PTR_ERR(profile);
++
++      mutex_lock(&aa_interface_lock);
++      write_lock(&profile_ns_list_lock);
++      if (e.ns_name)
++              ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
++      else
++              ns = default_namespace;
++      if (!ns) {
++              struct aa_namespace *new_ns;
++              write_unlock(&profile_ns_list_lock);
++              new_ns = alloc_aa_namespace(e.ns_name);
++              if (!new_ns) {
++                      mutex_unlock(&aa_interface_lock);
++                      return -ENOMEM;
++              }
++              write_lock(&profile_ns_list_lock);
++              ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
++              if (!ns) {
++                      list_add(&new_ns->list, &profile_ns_list);
++                      ns = new_ns;
++              } else
++                      free_aa_namespace(new_ns);
++      }
++
++      write_lock(&ns->lock);
++      if (__aa_find_profile(profile->name, &ns->profiles)) {
++              /* A profile with this name exists already. */
++              write_unlock(&ns->lock);
++              write_unlock(&profile_ns_list_lock);
++              sa.name = profile->name;
++              sa.name2 = ns->name;
++              sa.info = "failed: profile already loaded";
++              aa_audit_status(NULL, &sa);
++              mutex_unlock(&aa_interface_lock);
++              aa_put_profile(profile);
++              return -EEXIST;
++      }
++      profile->ns = aa_get_namespace(ns);
++      ns->profile_count++;
++      list_add(&profile->list, &ns->profiles);
++      write_unlock(&ns->lock);
++      write_unlock(&profile_ns_list_lock);
++
++      sa.name = profile->name;
++      sa.name2 = ns->name;
++      aa_audit_status(NULL, &sa);
++      mutex_unlock(&aa_interface_lock);
++      return size;
++}
++
++/**
++ * task_replace - replace a task's profile
++ * @task: task to replace profile on
++ * @new_cxt: new aa_task_context to do replacement with
++ * @new_profile: new profile
++ */
++static inline void task_replace(struct task_struct *task,
++                              struct aa_task_context *new_cxt,
++                              struct aa_profile *new_profile)
++{
++      struct aa_task_context *cxt = aa_task_context(task);
++
++      AA_DEBUG("%s: replacing profile for task %d "
++               "profile=%s (%p)\n",
++               __FUNCTION__,
++               cxt->task->pid,
++               cxt->profile->name, cxt->profile);
++
++      aa_change_task_context(task, new_cxt, new_profile, cxt->cookie,
++                             cxt->previous_profile);
++}
++
++/**
++ * aa_replace_profile - 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 aa_task_context.  If the profile does not exist on the profile list
++ * it is added.  Return %0 or error.
++ */
++ssize_t aa_replace_profile(void *udata, size_t size)
++{
++      struct aa_profile *old_profile, *new_profile;
++      struct aa_namespace *ns;
++      struct aa_task_context *new_cxt;
++      struct aa_ext e = {
++              .start = udata,
++              .end = udata + size,
++              .pos = udata,
++              .ns_name = NULL
++      };
++      ssize_t error;
++      struct aa_audit sa;
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "profile_replace";
++      sa.gfp_mask = GFP_KERNEL;
++
++      error = aa_verify_header(&e, &sa);
++      if (error)
++              return error;
++
++      new_profile = aa_unpack_profile(&e, &sa);
++      if (IS_ERR(new_profile))
++              return PTR_ERR(new_profile);
++
++      mutex_lock(&aa_interface_lock);
++      write_lock(&profile_ns_list_lock);
++      if (e.ns_name)
++              ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
++      else
++              ns = default_namespace;
++      if (!ns) {
++              struct aa_namespace *new_ns;
++              write_unlock(&profile_ns_list_lock);
++              new_ns = alloc_aa_namespace(e.ns_name);
++              if (!new_ns) {
++                      mutex_unlock(&aa_interface_lock);
++                      return -ENOMEM;
++              }
++              write_lock(&profile_ns_list_lock);
++              ns = __aa_find_namespace(e.ns_name, &profile_ns_list);
++              if (!ns) {
++                      list_add(&new_ns->list, &profile_ns_list);
++                      ns = new_ns;
++              } else
++                      free_aa_namespace(new_ns);
++      }
++
++      write_lock(&ns->lock);
++      old_profile = __aa_find_profile(new_profile->name, &ns->profiles);
++      if (old_profile) {
++              lock_profile(old_profile);
++              old_profile->isstale = 1;
++              list_del_init(&old_profile->list);
++              unlock_profile(old_profile);
++              ns->profile_count--;
++      }
++      new_profile->ns = aa_get_namespace(ns);
++      ns->profile_count++;
++      /* not don't need an extra ref count to keep new_profile as
++       * it is protect by the interface mutex */
++      list_add(&new_profile->list, &ns->profiles);
++      write_unlock(&ns->lock);
++      write_unlock(&profile_ns_list_lock);
++
++      if (!old_profile) {
++              sa.operation = "profile_load";
++              goto out;
++      }
++      /*
++       * Replacement needs to allocate a new aa_task_context for each
++       * task confined by old_profile.  To do this the profile locks
++       * are only held when the actual switch is done per task.  While
++       * looping to allocate a new aa_task_context the old_task list
++       * may get shorter if tasks exit/change their profile but will
++       * not get longer as new task will not use old_profile detecting
++       * that is stale.
++       */
++      do {
++              new_cxt = aa_alloc_task_context(GFP_KERNEL | __GFP_NOFAIL);
++
++              lock_both_profiles(old_profile, new_profile);
++              if (!list_empty(&old_profile->task_contexts)) {
++                      struct task_struct *task =
++                              list_entry(old_profile->task_contexts.next,
++                                         struct aa_task_context, list)->task;
++                      task_lock(task);
++                      task_replace(task, new_cxt, new_profile);
++                      task_unlock(task);
++                      new_cxt = NULL;
++              }
++              unlock_both_profiles(old_profile, new_profile);
++      } while (!new_cxt);
++      aa_free_task_context(new_cxt);
++      aa_put_profile(old_profile);
++
++out:
++      sa.name = new_profile->name;
++      sa.name2 = ns->name;
++      aa_audit_status(NULL, &sa);
++      mutex_unlock(&aa_interface_lock);
++      return size;
++}
++
++/**
++ * aa_remove_profile - 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 aa_task_context references
++ * to said profile.
++ */
++ssize_t aa_remove_profile(char *name, size_t size)
++{
++      struct aa_namespace *ns;
++      struct aa_profile *profile;
++      struct aa_audit sa;
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "profile_remove";
++      sa.gfp_mask = GFP_KERNEL;
++
++      mutex_lock(&aa_interface_lock);
++      write_lock(&profile_ns_list_lock);
++
++      if (name[0] == ':') {
++              char *split = strchr(name + 1, ':');
++              if (!split)
++                      goto noent;
++              *split = 0;
++              ns = __aa_find_namespace(name + 1, &profile_ns_list);
++              name = split + 1;
++      } else {
++              ns = default_namespace;
++      }
++
++      if (!ns)
++              goto noent;
++      sa.name2 = ns->name;
++      write_lock(&ns->lock);
++      profile = __aa_find_profile(name, &ns->profiles);
++      if (!profile) {
++              write_unlock(&ns->lock);
++              goto noent;
++      }
++      sa.name = profile->name;
++
++      /* Remove the profile from each task context it is on. */
++      lock_profile(profile);
++      profile->isstale = 1;
++      aa_unconfine_tasks(profile);
++      list_del_init(&profile->list);
++      ns->profile_count--;
++      unlock_profile(profile);
++      /* Release the profile itself. */
++      write_unlock(&ns->lock);
++      /* check to see if the namespace has become stale */
++      if (ns != default_namespace && ns->profile_count == 0) {
++              list_del_init(&ns->list);
++              aa_put_namespace(ns);
++      }
++      write_unlock(&profile_ns_list_lock);
++
++      aa_audit_status(NULL, &sa);
++      mutex_unlock(&aa_interface_lock);
++      aa_put_profile(profile);
++
++      return size;
++
++noent:
++      write_unlock(&profile_ns_list_lock);
++      sa.info = "failed: profile does not exist";
++      aa_audit_status(NULL, &sa);
++      mutex_unlock(&aa_interface_lock);
++      return -ENOENT;
++}
++
++/**
++ * free_aa_namespace_kref - free aa_namespace by kref (see aa_put_namespace)
++ * @kr: kref callback for freeing of a namespace
++ */
++void free_aa_namespace_kref(struct kref *kref)
++{
++      struct aa_namespace *ns=container_of(kref, struct aa_namespace, count);
++
++      free_aa_namespace(ns);
++}
++
++/**
++ * alloc_aa_namespace - allocate, initialize and return a new namespace
++ * @name: a preallocated name
++ * Returns NULL on failure.
++ */
++struct aa_namespace *alloc_aa_namespace(char *name)
++{
++      struct aa_namespace *ns;
++
++      ns = kzalloc(sizeof(*ns), GFP_KERNEL);
++      AA_DEBUG("%s(%p)\n", __FUNCTION__, ns);
++      if (ns) {
++              ns->name = name;
++              INIT_LIST_HEAD(&ns->list);
++              INIT_LIST_HEAD(&ns->profiles);
++              kref_init(&ns->count);
++              rwlock_init(&ns->lock);
++
++              ns->null_complain_profile = alloc_aa_profile();
++              if (!ns->null_complain_profile) {
++                      if (!name)
++                              kfree(ns->name);
++                      kfree(ns);
++                      return NULL;
++              }
++              ns->null_complain_profile->name =
++                      kstrdup("null-complain-profile", GFP_KERNEL);
++              if (!ns->null_complain_profile->name) {
++                      free_aa_profile(ns->null_complain_profile);
++                      if (!name)
++                              kfree(ns->name);
++                      kfree(ns);
++                      return NULL;
++              }
++              ns->null_complain_profile->flags.complain = 1;
++              /* null_complain_profile doesn't contribute to ns ref count */
++              ns->null_complain_profile->ns = ns;
++      }
++      return ns;
++}
++
++/**
++ * free_aa_namespace - free a profile namespace
++ * @namespace: the namespace to free
++ *
++ * Free a namespace.  All references to the namespace must have been put.
++ * If the namespace was referenced by a profile confining a task,
++ * free_aa_namespace will be called indirectly (through free_aa_profile)
++ * from an rcu callback routine, so we must not sleep here.
++ */
++void free_aa_namespace(struct aa_namespace *ns)
++{
++      AA_DEBUG("%s(%p)\n", __FUNCTION__, ns);
++
++      if (!ns)
++              return;
++
++      /* namespace still contains profiles -- invalid */
++      if (!list_empty(&ns->profiles)) {
++              AA_ERROR("%s: internal error, "
++                       "namespace '%s' still contains profiles\n",
++                       __FUNCTION__,
++                       ns->name);
++              BUG();
++      }
++      if (!list_empty(&ns->list)) {
++              AA_ERROR("%s: internal error, "
++                       "namespace '%s' still on list\n",
++                       __FUNCTION__,
++                       ns->name);
++              BUG();
++      }
++      /* null_complain_profile doesn't contribute to ns ref counting */
++      ns->null_complain_profile->ns = NULL;
++      aa_put_profile(ns->null_complain_profile);
++      kfree(ns->name);
++      kfree(ns);
++}
++
++/**
++ * free_aa_profile_kref - free aa_profile by kref (called by aa_put_profile)
++ * @kr: kref callback for freeing of a profile
++ */
++void free_aa_profile_kref(struct kref *kref)
++{
++      struct aa_profile *p=container_of(kref, struct aa_profile, count);
++
++      free_aa_profile(p);
++}
++
++/**
++ * alloc_aa_profile - allocate, initialize and return a new profile
++ * Returns NULL on failure.
++ */
++struct aa_profile *alloc_aa_profile(void)
++{
++      struct aa_profile *profile;
++
++      profile = kzalloc(sizeof(*profile), GFP_KERNEL);
++      AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
++      if (profile) {
++              INIT_LIST_HEAD(&profile->list);
++              kref_init(&profile->count);
++              INIT_LIST_HEAD(&profile->task_contexts);
++              spin_lock_init(&profile->lock);
++      }
++      return profile;
++}
++
++/**
++ * free_aa_profile - free a profile
++ * @profile: the profile to free
++ *
++ * Free a profile, its hats and null_profile. All references to the profile,
++ * its hats and null_profile must have been put.
++ *
++ * If the profile was referenced from a task context, free_aa_profile() will
++ * be called from an rcu callback routine, so we must not sleep here.
++ */
++void free_aa_profile(struct aa_profile *profile)
++{
++      AA_DEBUG("%s(%p)\n", __FUNCTION__, profile);
++
++      if (!profile)
++              return;
++
++      /* profile is still on profile namespace list -- invalid */
++      if (!list_empty(&profile->list)) {
++              AA_ERROR("%s: internal error, "
++                       "profile '%s' still on global list\n",
++                       __FUNCTION__,
++                       profile->name);
++              BUG();
++      }
++      aa_put_namespace(profile->ns);
++
++      aa_match_free(profile->file_rules);
++
++      if (profile->name) {
++              AA_DEBUG("%s: %s\n", __FUNCTION__, profile->name);
++              kfree(profile->name);
++      }
++
++      kfree(profile);
++}
++
++/**
++ * aa_unconfine_tasks - remove tasks on a profile's task context list
++ * @profile: profile to remove tasks from
++ *
++ * Assumes that @profile lock is held.
++ */
++void aa_unconfine_tasks(struct aa_profile *profile)
++{
++      while (!list_empty(&profile->task_contexts)) {
++              struct task_struct *task =
++                      list_entry(profile->task_contexts.next,
++                                 struct aa_task_context, list)->task;
++              task_lock(task);
++              aa_change_task_context(task, NULL, NULL, 0, NULL);
++              task_unlock(task);
++      }
++}
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-network.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-network.diff
new file mode 100644 (file)
index 0000000..d906459
--- /dev/null
@@ -0,0 +1,408 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: Simplified network controls for AppArmor
+
+Simple network control determining which network families a confined
+application has access to.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ security/apparmor/Makefile           |    7 +
+ security/apparmor/apparmor.h         |    9 ++
+ security/apparmor/lsm.c              |  129 ++++++++++++++++++++++++++++++++++-
+ security/apparmor/main.c             |  107 ++++++++++++++++++++++++++++-
+ security/apparmor/module_interface.c |   26 ++++++-
+ 5 files changed, 271 insertions(+), 7 deletions(-)
+
+--- a/security/apparmor/Makefile
++++ b/security/apparmor/Makefile
+@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l
+ quiet_cmd_make-caps = GEN     $@
+ cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
+-$(obj)/main.o : $(obj)/capability_names.h
++quiet_cmd_make-af = GEN     $@
++cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2]  = \"\\1\",/p" $< | tr A-Z a-z > $@
++
++$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h
+ $(obj)/capability_names.h : $(srctree)/include/linux/capability.h
+       $(call cmd,make-caps)
++$(obj)/af_names.h : $(srctree)/include/linux/socket.h
++      $(call cmd,make-af)
+--- a/security/apparmor/apparmor.h
++++ b/security/apparmor/apparmor.h
+@@ -16,6 +16,8 @@
+ #include <linux/fs.h>
+ #include <linux/binfmts.h>
+ #include <linux/rcupdate.h>
++#include <linux/socket.h>
++#include <net/sock.h>
+ /*
+  * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
+@@ -212,6 +214,9 @@ struct aa_profile {
+       struct list_head task_contexts;
+       spinlock_t lock;
+       unsigned long int_flags;
++      u16 network_families[AF_MAX];
++      u16 audit_network[AF_MAX];
++      u16 quiet_network[AF_MAX];
+ };
+ extern struct list_head profile_ns_list;
+@@ -258,6 +263,7 @@ struct aa_audit {
+       int request_mask, denied_mask, audit_mask;
+       struct iattr *iattr;
+       pid_t task, parent;
++      int family, type, protocol;
+       int error_code;
+ };
+@@ -319,6 +325,9 @@ extern void aa_change_task_context(struc
+                                  struct aa_profile *previous_profile);
+ extern int aa_may_ptrace(struct aa_task_context *cxt,
+                        struct aa_profile *tracee);
++extern int aa_net_perm(struct aa_profile *profile, char *operation,
++                      int family, int type, int protocol);
++extern int aa_revalidate_sk(struct sock *sk, char *operation);
+ /* lsm.c */
+ extern int apparmor_initialized;
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -18,6 +18,7 @@
+ #include <linux/ctype.h>
+ #include <linux/sysctl.h>
+ #include <linux/audit.h>
++#include <net/sock.h>
+ #include "apparmor.h"
+ #include "inline.h"
+@@ -680,6 +681,117 @@ static void apparmor_task_free_security(
+       aa_release(task);
+ }
++static int apparmor_socket_create(int family, int type, int protocol, int kern)
++{
++      struct aa_profile *profile;
++      int error = 0;
++
++      if (kern)
++              return 0;
++
++      profile = aa_get_profile(current);
++      if (profile)
++              error = aa_net_perm(profile, "socket_create", family,
++                                                      type, protocol);
++      aa_put_profile(profile);
++
++      return error;
++}
++
++static int apparmor_socket_post_create(struct socket *sock, int family,
++                                      int type, int protocol, int kern)
++{
++      struct sock *sk = sock->sk;
++
++      if (kern)
++              return 0;
++
++      return aa_revalidate_sk(sk, "socket_post_create");
++}
++
++static int apparmor_socket_bind(struct socket *sock,
++                              struct sockaddr *address, int addrlen)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_bind");
++}
++
++static int apparmor_socket_connect(struct socket *sock,
++                                      struct sockaddr *address, int addrlen)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_connect");
++}
++
++static int apparmor_socket_listen(struct socket *sock, int backlog)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_listen");
++}
++
++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_accept");
++}
++
++static int apparmor_socket_sendmsg(struct socket *sock,
++                                      struct msghdr *msg, int size)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_sendmsg");
++}
++
++static int apparmor_socket_recvmsg(struct socket *sock,
++                                 struct msghdr *msg, int size, int flags)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_recvmsg");
++}
++
++static int apparmor_socket_getsockname(struct socket *sock)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_getsockname");
++}
++
++static int apparmor_socket_getpeername(struct socket *sock)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_getpeername");
++}
++
++static int apparmor_socket_getsockopt(struct socket *sock, int level,
++                                      int optname)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_getsockopt");
++}
++
++static int apparmor_socket_setsockopt(struct socket *sock, int level,
++                                      int optname)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_setsockopt");
++}
++
++static int apparmor_socket_shutdown(struct socket *sock, int how)
++{
++      struct sock *sk = sock->sk;
++
++      return aa_revalidate_sk(sk, "socket_shutdown");
++}
++
+ static int apparmor_getprocattr(struct task_struct *task, char *name,
+                               char **value)
+ {
+@@ -780,9 +892,6 @@ struct security_operations apparmor_ops
+       .capable =                      apparmor_capable,
+       .syslog =                       cap_syslog,
+-      .netlink_send =                 cap_netlink_send,
+-      .netlink_recv =                 cap_netlink_recv,
+-
+       .bprm_apply_creds =             cap_bprm_apply_creds,
+       .bprm_set_security =            apparmor_bprm_set_security,
+       .bprm_secureexec =              apparmor_bprm_secureexec,
+@@ -820,6 +929,20 @@ struct security_operations apparmor_ops
+       .getprocattr =                  apparmor_getprocattr,
+       .setprocattr =                  apparmor_setprocattr,
++
++      .socket_create =                apparmor_socket_create,
++      .socket_post_create =           apparmor_socket_post_create,
++      .socket_bind =                  apparmor_socket_bind,
++      .socket_connect =               apparmor_socket_connect,
++      .socket_listen =                apparmor_socket_listen,
++      .socket_accept =                apparmor_socket_accept,
++      .socket_sendmsg =               apparmor_socket_sendmsg,
++      .socket_recvmsg =               apparmor_socket_recvmsg,
++      .socket_getsockname =           apparmor_socket_getsockname,
++      .socket_getpeername =           apparmor_socket_getpeername,
++      .socket_getsockopt =            apparmor_socket_getsockopt,
++      .socket_setsockopt =            apparmor_socket_setsockopt,
++      .socket_shutdown =              apparmor_socket_shutdown,
+ };
+ void info_message(const char *str)
+--- a/security/apparmor/main.c
++++ b/security/apparmor/main.c
+@@ -14,6 +14,9 @@
+ #include <linux/audit.h>
+ #include <linux/mount.h>
+ #include <linux/ptrace.h>
++#include <linux/socket.h>
++#include <linux/net.h>
++#include <net/sock.h>
+ #include "apparmor.h"
+@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au
+       audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
+ }
++static const char *address_families[] = {
++#include "af_names.h"
++};
++
++static const char *sock_types[] = {
++      "unknown(0)",
++      "stream",
++      "dgram",
++      "raw",
++      "rdm",
++      "seqpacket",
++      "dccp",
++      "unknown(7)",
++      "unknown(8)",
++      "unknown(9)",
++      "packet",
++};
++
+ /**
+  * aa_audit - Log an audit event to the audit subsystem
+  * @profile: profile to check against
+@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi
+               audit_log_untrustedstring(ab, sa->name2);
+       }
+-      audit_log_format(ab, " pid=%d", current->pid);
++      if (sa->family || sa->type) {
++              if (address_families[sa->family])
++                      audit_log_format(ab, " family=\"%s\"",
++                                       address_families[sa->family]);
++              else
++                      audit_log_format(ab, " family=\"unknown(%d)\"",
++                                       sa->family);
++
++              if (sock_types[sa->type])
++                      audit_log_format(ab, " sock_type=\"%s\"",
++                                       sock_types[sa->type]);
++              else
++                      audit_log_format(ab, " sock_type=\"unknown(%d)\"",
++                                       sa->type);
++
++              audit_log_format(ab, " protocol=%d", sa->protocol);
++      }
++
++        audit_log_format(ab, " pid=%d", current->pid);
+       if (profile) {
+               audit_log_format(ab, " profile=");
+@@ -767,6 +806,72 @@ int aa_link(struct aa_profile *profile,
+       return error;
+ }
++
++int aa_net_perm(struct aa_profile *profile, char *operation,
++              int family, int type, int protocol)
++{
++      struct aa_audit sa;
++      int error = 0;
++      u16 family_mask, audit_mask, quiet_mask;
++
++      if ((family < 0) || (family >= AF_MAX))
++              return -EINVAL;
++
++      if ((type < 0) || (type >= SOCK_MAX))
++              return -EINVAL;
++
++      /* unix domain and netlink sockets are handled by ipc */
++      if (family == AF_UNIX || family == AF_NETLINK)
++              return 0;
++
++      family_mask = profile->network_families[family];
++      audit_mask = profile->audit_network[family];
++      quiet_mask = profile->quiet_network[family];
++
++      error = (family_mask & (1 << type)) ? 0 : -EACCES;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = operation;
++      sa.gfp_mask = GFP_KERNEL;
++      sa.family = family;
++      sa.type = type;
++      sa.protocol = protocol;
++      sa.error_code = error;
++
++      if (likely(!error)) {
++              if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask))
++                      return 0;
++      } else if (!((1 << type) & ~quiet_mask)) {
++              return error;
++      }
++
++      error = aa_audit(profile, &sa);
++
++      return error;
++}
++
++int aa_revalidate_sk(struct sock *sk, char *operation)
++{
++      struct aa_profile *profile;
++      int error = 0;
++
++      /* this is some debugging code to flush out the network hooks that
++         that are called in interrupt context */
++      if (in_interrupt()) {
++              printk("AppArmor Debug: Hook being called from interrupt context\n");
++              dump_stack();
++              return 0;
++      }
++
++      profile = aa_get_profile(current);
++      if (profile)
++              error = aa_net_perm(profile, operation,
++                                  sk->sk_family, sk->sk_type,
++                                  sk->sk_protocol);
++      aa_put_profile(profile);
++
++      return error;
++}
+ /*******************************
+  * Global task related functions
+--- a/security/apparmor/module_interface.c
++++ b/security/apparmor/module_interface.c
+@@ -321,8 +321,8 @@ static struct aa_profile *aa_unpack_prof
+                                           struct aa_audit *sa)
+ {
+       struct aa_profile *profile = NULL;
+-
+-      int error = -EPROTO;
++      size_t size = 0;
++      int i, error = -EPROTO;
+       profile = alloc_aa_profile();
+       if (!profile)
+@@ -355,6 +355,28 @@ static struct aa_profile *aa_unpack_prof
+       if (!aa_is_u32(e, &(profile->set_caps), NULL))
+               goto fail;
++      size = aa_is_array(e, "net_allowed_af");
++      if (size) {
++              if (size > AF_MAX)
++                      goto fail;
++
++              for (i = 0; i < size; i++) {
++                      if (!aa_is_u16(e, &profile->network_families[i], NULL))
++                              goto fail;
++                      if (!aa_is_u16(e, &profile->audit_network[i], NULL))
++                              goto fail;
++                      if (!aa_is_u16(e, &profile->quiet_network[i], NULL))
++                              goto fail;
++              }
++              if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
++                      goto fail;
++              /* allow unix domain and netlink sockets they are handled
++               * by IPC
++               */
++      }
++      profile->network_families[AF_UNIX] = 0xffff;
++      profile->network_families[AF_NETLINK] = 0xffff;
++
+       /* get file rules */
+       profile->file_rules = aa_unpack_dfa(e);
+       if (IS_ERR(profile->file_rules)) {
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-ptrace-2.6.27.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-ptrace-2.6.27.diff
new file mode 100644 (file)
index 0000000..c80f740
--- /dev/null
@@ -0,0 +1,55 @@
+From: Jeff Mahoney <jeffm@suse.com>
+Subject: apparmor: use new ptrace security_operations
+
+ This patch implements the new ptrace security_operations members.
+
+ ->ptrace was changed to ->ptrace_may_access and ->ptrace_traceme.
+
+ The apparmor versions are really just wrappers for the old function.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+
+---
+ security/apparmor/lsm.c |   17 +++++++++++++++--
+ 1 file changed, 15 insertions(+), 2 deletions(-)
+
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -158,7 +158,7 @@ static int aa_reject_syscall(struct task
+ }
+ static int apparmor_ptrace(struct task_struct *parent,
+-                         struct task_struct *child, unsigned int mode)
++                         struct task_struct *child)
+ {
+       struct aa_task_context *cxt;
+       int error = 0;
+@@ -207,6 +207,18 @@ static int apparmor_ptrace(struct task_s
+       return error;
+ }
++static int apparmor_ptrace_may_access(struct task_struct *child,
++                                    unsigned int mode)
++{
++      return apparmor_ptrace(child->parent, child);
++}
++
++
++static int apparmor_ptrace_traceme(struct task_struct *parent)
++{
++      return apparmor_ptrace(parent, current);
++}
++
+ static int apparmor_capable(struct task_struct *task, int cap)
+ {
+       int error;
+@@ -899,7 +911,8 @@ static int apparmor_task_setrlimit(unsig
+ }
+ struct security_operations apparmor_ops = {
+-      .ptrace =                       apparmor_ptrace,
++      .ptrace_may_access =            apparmor_ptrace_may_access,
++      .ptrace_traceme =               apparmor_ptrace_traceme,
+       .capget =                       cap_capget,
+       .capset_check =                 cap_capset_check,
+       .capset_set =                   cap_capset_set,
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-rlimits.diff b/src/patches/suse-2.6.27.39/patches.apparmor/apparmor-rlimits.diff
new file mode 100644 (file)
index 0000000..f3912fd
--- /dev/null
@@ -0,0 +1,461 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: per profile controls for system rlimits
+
+Provide contol of rlimits on a per profile basis.  Each profile provides
+a per limit contol and corresponding hard limit value, such that when a
+profile becomes attached to a task it sets the tasks limits to be <= to
+the profiles specified limits.  Note: the profile limit value will not
+raise a tasks limit if it is already less than the profile mandates.
+
+In addition to setting a tasks limits, the ability to set limits on
+a confined task are controlled.  AppArmor only controls the raising
+of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits
+raised up to the value specified by the profile.  AppArmor does not
+prevent a task for lowering its hard limits, nor does it provide
+additional control on soft limits.
+
+AppArmor only controls the limits specified in a profile so that
+any limit not specified is free to be modified subject to standard
+linux limitations.
+
+---
+ security/apparmor/apparmor.h         |   23 ++++++
+ security/apparmor/apparmorfs.c       |    2 
+ security/apparmor/lsm.c              |   16 ++++
+ security/apparmor/main.c             |  132 +++++++++++++++++++++++++++++++----
+ security/apparmor/module_interface.c |   56 ++++++++++++++
+ 5 files changed, 215 insertions(+), 14 deletions(-)
+
+--- a/security/apparmor/apparmor.h
++++ b/security/apparmor/apparmor.h
+@@ -16,6 +16,7 @@
+ #include <linux/fs.h>
+ #include <linux/binfmts.h>
+ #include <linux/rcupdate.h>
++#include <linux/resource.h>
+ #include <linux/socket.h>
+ #include <net/sock.h>
+@@ -139,6 +140,18 @@ extern unsigned int apparmor_path_max;
+ #define AA_ERROR(fmt, args...)        printk(KERN_ERR "AppArmor: " fmt, ##args)
++/* struct aa_rlimit - rlimits settings for the profile
++ * @mask: which hard limits to set
++ * @limits: rlimit values that override task limits
++ *
++ * AppArmor rlimits are used to set confined task rlimits.  Only the
++ * limits specified in @mask will be controlled by apparmor.
++ */
++struct aa_rlimit {
++      unsigned int mask;
++      struct rlimit limits[RLIM_NLIMITS];
++};
++
+ struct aa_profile;
+ /* struct aa_namespace - namespace for a set of profiles
+@@ -173,6 +186,8 @@ struct aa_namespace {
+  * @audit_caps: caps that are to be audited
+  * @quiet_caps: caps that should not be audited
+  * @capabilities: capabilities granted by the process
++ * @rlimits: rlimits for the profile
++ * @task_count: how many tasks the profile is attached to
+  * @count: reference count of the profile
+  * @task_contexts: list of tasks confined by profile
+  * @lock: lock for the task_contexts list
+@@ -210,6 +225,9 @@ struct aa_profile {
+       kernel_cap_t audit_caps;
+       kernel_cap_t quiet_caps;
++      struct aa_rlimit rlimits;
++      unsigned int task_count;
++
+       struct kref count;
+       struct list_head task_contexts;
+       spinlock_t lock;
+@@ -261,6 +279,7 @@ struct aa_audit {
+       const char *name2;
+       const char *name3;
+       int request_mask, denied_mask, audit_mask;
++      int rlimit;
+       struct iattr *iattr;
+       pid_t task, parent;
+       int family, type, protocol;
+@@ -328,6 +347,10 @@ extern int aa_may_ptrace(struct aa_task_
+ extern int aa_net_perm(struct aa_profile *profile, char *operation,
+                       int family, int type, int protocol);
+ extern int aa_revalidate_sk(struct sock *sk, char *operation);
++extern int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
++                           struct rlimit *new_rlim);
++extern void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile);
++
+ /* lsm.c */
+ extern int apparmor_initialized;
+--- a/security/apparmor/apparmorfs.c
++++ b/security/apparmor/apparmorfs.c
+@@ -106,7 +106,7 @@ static ssize_t aa_features_read(struct f
+ {
+       const char *features = "file=3.0 capability=2.0 network=1.0 "
+                              "change_hat=1.5 change_profile=1.0 "
+-                             "aanamespaces=1.0";
++                             "aanamespaces=1.0 rlimit=1.0";
+       return simple_read_from_buffer(buf, size, ppos, features,
+                                      strlen(features));
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -883,6 +883,21 @@ static int apparmor_setprocattr(struct t
+       return error;
+ }
++static int apparmor_task_setrlimit(unsigned int resource,
++                                 struct rlimit *new_rlim)
++{
++      struct aa_profile *profile;
++      int error = 0;
++
++      profile = aa_get_profile(current);
++      if (profile) {
++              error = aa_task_setrlimit(profile, resource, new_rlim);
++      }
++      aa_put_profile(profile);
++
++      return error;
++}
++
+ struct security_operations apparmor_ops = {
+       .ptrace =                       apparmor_ptrace,
+       .capget =                       cap_capget,
+@@ -926,6 +941,7 @@ struct security_operations apparmor_ops
+       .task_free_security =           apparmor_task_free_security,
+       .task_post_setuid =             cap_task_post_setuid,
+       .task_reparent_to_init =        cap_task_reparent_to_init,
++      .task_setrlimit =               apparmor_task_setrlimit,
+       .getprocattr =                  apparmor_getprocattr,
+       .setprocattr =                  apparmor_setprocattr,
+--- a/security/apparmor/main.c
++++ b/security/apparmor/main.c
+@@ -177,6 +177,9 @@ static int aa_audit_base(struct aa_profi
+       if (sa->request_mask)
+               audit_log_format(ab, " fsuid=%d", current->fsuid);
++      if (sa->rlimit)
++              audit_log_format(ab, " rlimit=%d", sa->rlimit - 1);
++
+       if (sa->iattr) {
+               struct iattr *iattr = sa->iattr;
+@@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch
+       return error;
+ }
++/**
++ * aa_task_setrlimit - test permission to set an rlimit
++ * @profile - profile confining the task
++ * @resource - the resource being set
++ * @new_rlim - the new resource limit
++ *
++ * Control raising the processes hard limit.
++ */
++int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
++                    struct rlimit *new_rlim)
++{
++      struct aa_audit sa;
++      int error = 0;
++
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "setrlimit";
++      sa.gfp_mask = GFP_KERNEL;
++      sa.rlimit = resource + 1;
++
++      if (profile->rlimits.mask & (1 << resource) &&
++          new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) {
++              sa.error_code = -EACCES;
++
++              error = aa_audit(profile, &sa);
++      }
++
++      return error;
++}
++
++static int aa_rlimit_nproc(struct aa_profile *profile) {
++      if (profile && (profile->rlimits.mask & (1 << RLIMIT_NPROC)) &&
++          profile->task_count >= profile->rlimits.limits[RLIMIT_NPROC].rlim_max)
++              return -EAGAIN;
++      return 0;
++}
++
++void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile)
++{
++      int i, mask;
++
++      if (!profile)
++              return;
++
++      if (!profile->rlimits.mask)
++              return;
++
++      task_lock(task->group_leader);
++      mask = 1;
++      for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) {
++              struct rlimit new_rlim, *old_rlim;
++
++              /* check to see if NPROC which is per profile and handled
++               * in clone/exec or whether this is a limit to be set
++               * can't set cpu limit either right now
++               */
++              if (i == RLIMIT_NPROC || i == RLIMIT_CPU)
++                      continue;
++
++              old_rlim = task->signal->rlim + i;
++              new_rlim = *old_rlim;
++
++              if (mask & profile->rlimits.mask &&
++                  profile->rlimits.limits[i].rlim_max < new_rlim.rlim_max) {
++                      new_rlim.rlim_max = profile->rlimits.limits[i].rlim_max;
++                      /* soft limit should not exceed hard limit */
++                      if (new_rlim.rlim_cur > new_rlim.rlim_max)
++                              new_rlim.rlim_cur = new_rlim.rlim_max;
++              }
++
++              *old_rlim = new_rlim;
++      }
++      task_unlock(task->group_leader);
++}
+ /*******************************
+  * Global task related functions
+@@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch
+  */
+ int aa_clone(struct task_struct *child)
+ {
++      struct aa_audit sa;
+       struct aa_task_context *cxt, *child_cxt;
+       struct aa_profile *profile;
+@@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child)
+       if (!child_cxt)
+               return -ENOMEM;
++      memset(&sa, 0, sizeof(sa));
++      sa.operation = "clone";
++      sa.task = child->pid;
++      sa.gfp_mask = GFP_KERNEL;
++
+ repeat:
+       profile = aa_get_profile(current);
+       if (profile) {
+@@ -910,18 +992,22 @@ repeat:
+                       goto repeat;
+               }
++              if (aa_rlimit_nproc(profile)) {
++                      sa.info = "rlimit nproc limit exceeded";
++                      unlock_profile(profile);
++                      aa_audit_reject(profile, &sa);
++                      aa_put_profile(profile);
++                      return -EAGAIN;
++              }
++
+               /* No need to grab the child's task lock here. */
+               aa_change_task_context(child, child_cxt, profile,
+                                      cxt->cookie, cxt->previous_profile);
++
+               unlock_profile(profile);
+               if (APPARMOR_COMPLAIN(child_cxt) &&
+                   profile == profile->ns->null_complain_profile) {
+-                      struct aa_audit sa;
+-                      memset(&sa, 0, sizeof(sa));
+-                      sa.operation = "clone";
+-                      sa.gfp_mask = GFP_KERNEL;
+-                      sa.task = child->pid;
+                       aa_audit_hint(profile, &sa);
+               }
+               aa_put_profile(profile);
+@@ -1156,6 +1242,10 @@ repeat:
+                       sa.task = current->parent->pid;
+                       aa_audit_reject(profile, &sa);
+               }
++              if (PTR_ERR(old_profile) == -EAGAIN) {
++                      sa.info = "rlimit nproc limit exceeded";
++                      aa_audit_reject(profile, &sa);
++              }
+               new_profile = old_profile;
+               goto cleanup;
+       }
+@@ -1303,6 +1393,12 @@ static int do_change_profile(struct aa_p
+               goto out;
+       }
++      if ((error = aa_rlimit_nproc(new_profile))) {
++              sa->info = "rlimit nproc limit exceeded";
++              aa_audit_reject(cxt->profile, sa);
++              goto out;
++      }
++
+       if (new_profile == ns->null_complain_profile)
+               aa_audit_hint(cxt->profile, sa);
+@@ -1481,17 +1577,18 @@ struct aa_profile *__aa_replace_profile(
+       cxt = lock_task_and_profiles(task, profile);
+       if (unlikely(profile && profile->isstale)) {
+-              task_unlock(task);
+-              unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+-              aa_free_task_context(new_cxt);
+-              return ERR_PTR(-ESTALE);
++              old_profile = ERR_PTR(-ESTALE);
++              goto error;
+       }
+       if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
+-              task_unlock(task);
+-              unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+-              aa_free_task_context(new_cxt);
+-              return ERR_PTR(-EPERM);
++              old_profile = ERR_PTR(-EPERM);
++              goto error;
++      }
++
++      if (aa_rlimit_nproc(profile)) {
++              old_profile = ERR_PTR(-EAGAIN);
++              goto error;
+       }
+       if (cxt)
+@@ -1499,8 +1596,15 @@ struct aa_profile *__aa_replace_profile(
+       aa_change_task_context(task, new_cxt, profile, 0, NULL);
+       task_unlock(task);
++      aa_set_rlimits(task, profile);
+       unlock_both_profiles(profile, old_profile);
+       return old_profile;
++
++error:
++      task_unlock(task);
++      unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
++      aa_free_task_context(new_cxt);
++      return old_profile;
+ }
+ /**
+@@ -1565,6 +1669,7 @@ void aa_change_task_context(struct task_
+       if (old_cxt) {
+               list_del_init(&old_cxt->list);
++              old_cxt->profile->task_count--;
+               call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
+       }
+       if (new_cxt) {
+@@ -1576,6 +1681,7 @@ void aa_change_task_context(struct task_
+               new_cxt->cookie = cookie;
+               new_cxt->task = task;
+               new_cxt->profile = aa_dup_profile(profile);
++              profile->task_count++;
+               new_cxt->previous_profile = aa_dup_profile(previous_profile);
+               list_move(&new_cxt->list, &profile->task_contexts);
+       }
+--- a/security/apparmor/module_interface.c
++++ b/security/apparmor/module_interface.c
+@@ -177,6 +177,22 @@ fail:
+       return 0;
+ }
++static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name)
++{
++      void *pos = e->pos;
++      if (aa_is_nameX(e, AA_U64, name)) {
++              if (!aa_inbounds(e, sizeof(u64)))
++                      goto fail;
++              if (data)
++                      *data = le64_to_cpu(get_unaligned((u64 *)e->pos));
++              e->pos += sizeof(u64);
++              return 1;
++      }
++fail:
++      e->pos = pos;
++      return 0;
++}
++
+ static size_t aa_is_array(struct aa_ext *e, const char *name)
+ {
+       void *pos = e->pos;
+@@ -312,6 +328,39 @@ fail:
+       return 0;
+ }
++int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
++{
++      void *pos = e->pos;
++
++      /* rlimits are optional */
++      if (aa_is_nameX(e, AA_STRUCT, "rlimits")) {
++              int i, size;
++              u32 tmp = 0;
++              if (!aa_is_u32(e, &tmp, NULL))
++                      goto fail;
++              profile->rlimits.mask = tmp;
++
++              size = aa_is_array(e, NULL);
++              if (size > RLIM_NLIMITS)
++                      goto fail;
++              for (i = 0; i < size; i++) {
++                      u64 tmp = 0;
++                      if (!aa_is_u64(e, &tmp, NULL))
++                              goto fail;
++                      profile->rlimits.limits[i].rlim_max = tmp;
++              }
++              if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
++                      goto fail;
++              if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
++                      goto fail;
++      }
++      return 1;
++
++fail:
++      e->pos = pos;
++      return 0;
++}
++
+ /**
+  * aa_unpack_profile - unpack a serialized profile
+  * @e: serialized data extent information
+@@ -355,6 +404,9 @@ static struct aa_profile *aa_unpack_prof
+       if (!aa_is_u32(e, &(profile->set_caps), NULL))
+               goto fail;
++      if (!aa_unpack_rlimits(e, profile))
++              goto fail;
++
+       size = aa_is_array(e, "net_allowed_af");
+       if (size) {
+               if (size > AF_MAX)
+@@ -614,6 +666,8 @@ ssize_t aa_replace_profile(void *udata,
+               sa.operation = "profile_load";
+               goto out;
+       }
++      /* do not fail replacement based off of profile's NPROC rlimit */
++
+       /*
+        * Replacement needs to allocate a new aa_task_context for each
+        * task confined by old_profile.  To do this the profile locks
+@@ -634,6 +688,7 @@ ssize_t aa_replace_profile(void *udata,
+                       task_lock(task);
+                       task_replace(task, new_cxt, new_profile);
+                       task_unlock(task);
++                      aa_set_rlimits(task, new_profile);
+                       new_cxt = NULL;
+               }
+               unlock_both_profiles(old_profile, new_profile);
+@@ -656,6 +711,7 @@ out:
+  *
+  * remove a profile from the profile list and all aa_task_context references
+  * to said profile.
++ * NOTE: removing confinement does not restore rlimits to preconfinemnet values
+  */
+ ssize_t aa_remove_profile(char *name, size_t size)
+ {
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/d_namespace_path.diff b/src/patches/suse-2.6.27.39/patches.apparmor/d_namespace_path.diff
new file mode 100644 (file)
index 0000000..e82cf5e
--- /dev/null
@@ -0,0 +1,60 @@
+From: Andreas Gruenbacher <agruen@suse.de>
+Subject: Add d_namespace_path() to compute namespace relative pathnames
+
+In AppArmor, we are interested in pathnames relative to the namespace root.
+This is the same as d_path() except for the root where the search ends. Add
+a function for computing the namespace-relative path.
+
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ fs/namespace.c        |   30 ++++++++++++++++++++++++++++++
+ include/linux/mount.h |    2 ++
+ 2 files changed, 32 insertions(+)
+
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -2357,3 +2357,33 @@ void put_mnt_ns(struct mnt_namespace *ns
+       kfree(ns);
+ }
+ EXPORT_SYMBOL(put_mnt_ns);
++
++char *d_namespace_path(struct dentry *dentry, struct vfsmount *vfsmnt,
++                     char *buf, int buflen)
++{
++      struct path root, tmp, ns_root = { };
++      struct path path = { .mnt = vfsmnt, .dentry = dentry };
++      char *res;
++
++      read_lock(&current->fs->lock);
++      root = current->fs->root;
++      path_get(&current->fs->root);
++      read_unlock(&current->fs->lock);
++      spin_lock(&vfsmount_lock);
++      if (root.mnt)
++              ns_root.mnt = mntget(root.mnt->mnt_ns->root);
++      if (ns_root.mnt)
++              ns_root.dentry = dget(ns_root.mnt->mnt_root);
++      spin_unlock(&vfsmount_lock);
++      tmp = ns_root;
++      res = __d_path(&path, &tmp, buf, buflen,
++                     D_PATH_FAIL_DELETED | D_PATH_DISCONNECT);
++      path_put(&root);
++      path_put(&ns_root);
++
++      /* Prevent empty path for lazily unmounted filesystems. */
++      if (!IS_ERR(res) && *res == '\0')
++              *--res = '.';
++      return res;
++}
++EXPORT_SYMBOL(d_namespace_path);
+--- a/include/linux/mount.h
++++ b/include/linux/mount.h
+@@ -137,4 +137,6 @@ extern void mark_mounts_for_expiry(struc
+ extern spinlock_t vfsmount_lock;
+ extern dev_t name_to_dev_t(char *name);
++extern char *d_namespace_path(struct dentry *, struct vfsmount *, char *, int);
++
+ #endif /* _LINUX_MOUNT_H */
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/d_namespace_path_oops_fix.diff b/src/patches/suse-2.6.27.39/patches.apparmor/d_namespace_path_oops_fix.diff
new file mode 100644 (file)
index 0000000..f1ae52d
--- /dev/null
@@ -0,0 +1,25 @@
+From: Miklos Szeredi <mszeredi@suse.cz>
+Subject: fix oops in d_namespace_path
+Patch-mainline: no
+References: bnc#433504
+
+d_namespace_path uses the current->fs->root to get the current
+namespace.  If root is detached root.mnt->mnt_ns will be NULL, causing
+an Oops.  Fix by checking this before dereferencing the mnt_ns.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+---
+ fs/namespace.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -2370,7 +2370,7 @@ char *d_namespace_path(struct dentry *de
+       path_get(&current->fs->root);
+       read_unlock(&current->fs->lock);
+       spin_lock(&vfsmount_lock);
+-      if (root.mnt)
++      if (root.mnt && root.mnt->mnt_ns)
+               ns_root.mnt = mntget(root.mnt->mnt_ns->root);
+       if (ns_root.mnt)
+               ns_root.dentry = dget(ns_root.mnt->mnt_root);
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/do_path_lookup-nameidata.diff b/src/patches/suse-2.6.27.39/patches.apparmor/do_path_lookup-nameidata.diff
new file mode 100644 (file)
index 0000000..44a21b9
--- /dev/null
@@ -0,0 +1,42 @@
+From: Andreas Gruenbacher <agruen@suse.de>
+Subject: Switch to vfs_permission() in do_path_lookup()
+
+Switch from file_permission() to vfs_permission() in do_path_lookup():
+this avoids calling permission() with a NULL nameidata here.
+
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ fs/namei.c |    7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -1085,24 +1085,21 @@ static int do_path_lookup(int dfd, const
+               path_get(&fs->pwd);
+               read_unlock(&fs->lock);
+       } else {
+-              struct dentry *dentry;
+-
+               file = fget_light(dfd, &fput_needed);
+               retval = -EBADF;
+               if (!file)
+                       goto out_fail;
+-              dentry = file->f_path.dentry;
++              nd->path = file->f_path;
+               retval = -ENOTDIR;
+-              if (!S_ISDIR(dentry->d_inode->i_mode))
++              if (!S_ISDIR(nd->path.dentry->d_inode->i_mode))
+                       goto fput_fail;
+               retval = file_permission(file, MAY_EXEC);
+               if (retval)
+                       goto fput_fail;
+-              nd->path = file->f_path;
+               path_get(&file->f_path);
+               fput_light(file, fput_needed);
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/file-handle-ops.diff b/src/patches/suse-2.6.27.39/patches.apparmor/file-handle-ops.diff
new file mode 100644 (file)
index 0000000..2e921f9
--- /dev/null
@@ -0,0 +1,84 @@
+From: Andreas Gruenbacher <agruen@suse.de>
+Subject: Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
+
+Struct iattr already contains ia_file since commit cc4e69de from 
+Miklos (which is related to commit befc649c). Use this to pass
+struct file down the setattr hooks. This allows LSMs to distinguish
+operations on file descriptors from operations on paths.
+
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Cc: Miklos Szeredi <mszeredi@suse.cz>
+
+---
+ fs/nfsd/vfs.c |   12 +++++++-----
+ fs/open.c     |    5 ++++-
+ 2 files changed, 11 insertions(+), 6 deletions(-)
+
+--- a/fs/nfsd/vfs.c
++++ b/fs/nfsd/vfs.c
+@@ -425,7 +425,7 @@ static ssize_t nfsd_getxattr(struct dent
+ {
+       ssize_t buflen;
+-      buflen = vfs_getxattr(dentry, mnt, key, NULL, 0);
++      buflen = vfs_getxattr(dentry, mnt, key, NULL, 0, NULL);
+       if (buflen <= 0)
+               return buflen;
+@@ -433,7 +433,7 @@ static ssize_t nfsd_getxattr(struct dent
+       if (!*buf)
+               return -ENOMEM;
+-      return vfs_getxattr(dentry, mnt, key, *buf, buflen);
++      return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL);
+ }
+ #endif
+@@ -459,7 +459,7 @@ set_nfsv4_acl_one(struct dentry *dentry,
+               goto out;
+       }
+-      error = vfs_setxattr(dentry, mnt, key, buf, len, 0);
++      error = vfs_setxattr(dentry, mnt, key, buf, len, 0, NULL);
+ out:
+       kfree(buf);
+       return error;
+@@ -2133,12 +2133,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
+       if (error)
+               goto getout;
+       if (size)
+-              error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size,0);
++              error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0,
++                                   NULL);
+       else {
+               if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
+                       error = 0;
+               else {
+-                      error = vfs_removexattr(fhp->fh_dentry, mnt, name);
++                      error = vfs_removexattr(fhp->fh_dentry, mnt, name,
++                                              NULL);
+                       if (error == -ENODATA)
+                               error = 0;
+               }
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -623,7 +623,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
+       if (mode == (mode_t) -1)
+               mode = inode->i_mode;
+       newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+-      newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
++      newattrs.ia_valid = ATTR_MODE | ATTR_CTIME | ATTR_FILE;
+       err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
+       mutex_unlock(&inode->i_mutex);
+       mnt_drop_write(file->f_path.mnt);
+@@ -686,6 +686,9 @@ static int chown_common(struct dentry * 
+       if (!S_ISDIR(inode->i_mode))
+               newattrs.ia_valid |=
+                       ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
++      if (file)
++              newattrs.ia_valid |= ATTR_FILE;
++
+       mutex_lock(&inode->i_mutex);
+       error = fnotify_change(dentry, mnt, &newattrs, file);
+       mutex_unlock(&inode->i_mutex);
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fix-complain.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fix-complain.diff
new file mode 100644 (file)
index 0000000..85feac1
--- /dev/null
@@ -0,0 +1,26 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: fix enforcement of deny rules in complain mode
+Patch-mainline: no
+References: bnc#426159
+
+Fix enforcement of deny rules so that they are not enforced in complain
+mode.  This is necessary so that application behavior is not changed by
+the presence of the deny rule.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ security/apparmor/main.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/security/apparmor/main.c
++++ b/security/apparmor/main.c
+@@ -325,7 +325,7 @@ static int aa_audit_file(struct aa_profi
+       } else {
+               int mask = AUDIT_QUIET_MASK(sa->audit_mask);
+-              if (!(sa->denied_mask & ~mask))
++              if (!(sa->denied_mask & ~mask) && !PROFILE_COMPLAIN(profile))
+                       return sa->error_code;
+               /* mask off perms whose denial is being silenced */
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fix-deleted-leak.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fix-deleted-leak.diff
new file mode 100644 (file)
index 0000000..f70fedc
--- /dev/null
@@ -0,0 +1,25 @@
+From: John Johansen <jrjohansen@verizon.net>
+Subject: [PATCH] AppArmor: Fix leak of filename for deleted files
+
+ This patch fixes a memory leak where the name doesn't get freed when
+ a file has been deleted.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+---
+ security/apparmor/main.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/security/apparmor/main.c
++++ b/security/apparmor/main.c
+@@ -500,10 +500,10 @@ static char *aa_get_name(struct dentry *
+                       *buffer = buf;
+                       return name;
+               }
++              kfree(buf);
+               if (PTR_ERR(name) != -ENAMETOOLONG)
+                       return name;
+-              kfree(buf);
+               size <<= 1;
+               if (size > apparmor_path_max)
+                       return ERR_PTR(-ENAMETOOLONG);
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fix-security-param.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fix-security-param.diff
new file mode 100644 (file)
index 0000000..8d03cbc
--- /dev/null
@@ -0,0 +1,66 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: fix recognition of security= boot parameter
+Patch-mainline: no
+References: bnc#442668
+
+Fix AppArmor to respect the kernel boot parameter security=, so that if a
+different lsm is choosen apparmor does not try to register its lsm hooks.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ security/Kconfig        |    9 +++++++++
+ security/apparmor/lsm.c |    5 +++--
+ security/security.c     |    2 +-
+ 3 files changed, 13 insertions(+), 3 deletions(-)
+
+--- a/security/Kconfig
++++ b/security/Kconfig
+@@ -51,6 +51,15 @@ config SECURITY
+         If you are unsure how to answer this question, answer N.
++config SECURITY_DEFAULT
++      string "Default security module"
++      depends on SECURITY
++      default ""
++      help
++          This determines the security module used if the security=
++          boot parmater is not provided.  If a security module is not
++          specified the first module to register will be used.
++
+ config SECURITY_NETWORK
+       bool "Socket and Networking Security Hooks"
+       depends on SECURITY
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -911,6 +911,7 @@ static int apparmor_task_setrlimit(unsig
+ }
+ struct security_operations apparmor_ops = {
++      .name =                         "apparmor",
+       .ptrace_may_access =            apparmor_ptrace_may_access,
+       .ptrace_traceme =               apparmor_ptrace_traceme,
+       .capget =                       cap_capget,
+@@ -989,8 +990,8 @@ static int __init apparmor_init(void)
+ {
+       int error;
+-      if (!apparmor_enabled) {
+-              info_message("AppArmor disabled by boottime parameter\n");
++      if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) {
++              info_message("AppArmor disabled by boot time parameter\n");
+               return 0;
+       }
+--- a/security/security.c
++++ b/security/security.c
+@@ -18,7 +18,7 @@
+ #include <linux/security.h>
+ /* Boot-time LSM user choice */
+-static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1];
++static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_SECURITY_DEFAULT;
+ /* things that live in capability.c */
+ extern struct security_operations default_security_ops;
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fix-vfs_rmdir.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fix-vfs_rmdir.diff
new file mode 100644 (file)
index 0000000..141bc64
--- /dev/null
@@ -0,0 +1,44 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: Call lsm hook before unhashing dentry in vfs_rmdir()
+
+If we unhash the dentry before calling the security_inode_rmdir hook,
+we cannot compute the file's pathname in the hook anymore. AppArmor
+needs to know the filename in order to decide whether a file may be
+deleted, though.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+
+---
+ fs/namei.c |   13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -2177,6 +2177,10 @@ int vfs_rmdir(struct inode *dir, struct 
+       if (!dir->i_op || !dir->i_op->rmdir)
+               return -EPERM;
++      error = security_inode_rmdir(dir, dentry, mnt);
++      if (error)
++              return error;
++
+       DQUOT_INIT(dir);
+       mutex_lock(&dentry->d_inode->i_mutex);
+@@ -2184,12 +2188,9 @@ int vfs_rmdir(struct inode *dir, struct 
+       if (d_mountpoint(dentry))
+               error = -EBUSY;
+       else {
+-              error = security_inode_rmdir(dir, dentry, mnt);
+-              if (!error) {
+-                      error = dir->i_op->rmdir(dir, dentry);
+-                      if (!error)
+-                              dentry->d_inode->i_flags |= S_DEAD;
+-              }
++              error = dir->i_op->rmdir(dir, dentry);
++              if (!error)
++                      dentry->d_inode->i_flags |= S_DEAD;
+       }
+       mutex_unlock(&dentry->d_inode->i_mutex);
+       if (!error) {
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fork-tracking.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fork-tracking.diff
new file mode 100644 (file)
index 0000000..49cbd88
--- /dev/null
@@ -0,0 +1,108 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: fix log messages to enable tools profile learning
+Patch-mainline: no
+References: bnc#447564  
+
+The allocation of the child pid is done after the LSM clone hook, which
+breaks the AppArmor tools fork tracking, for profiles learning.  Output
+the parent pid with each log message to enable the tools to handle fork
+tracking.
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ security/apparmor/lsm.c              |   28 ----------------------------
+ security/apparmor/main.c             |   10 +++++-----
+ security/apparmor/module_interface.c |    2 +-
+ 3 files changed, 6 insertions(+), 34 deletions(-)
+
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -143,20 +143,6 @@ static int param_set_aa_enabled(const ch
+       return 0;
+ }
+-static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
+-                           const char *name)
+-{
+-      struct aa_profile *profile = aa_get_profile(task);
+-      int error = 0;
+-
+-      if (profile) {
+-              error = aa_audit_syscallreject(profile, flags, name);
+-              aa_put_profile(profile);
+-      }
+-
+-      return error;
+-}
+-
+ static int apparmor_ptrace(struct task_struct *parent,
+                          struct task_struct *child)
+ {
+@@ -292,17 +278,6 @@ static int apparmor_bprm_secureexec(stru
+       return ret;
+ }
+-static int apparmor_sb_mount(char *dev_name, struct path *path, char *type,
+-                            unsigned long flags, void *data)
+-{
+-      return aa_reject_syscall(current, GFP_KERNEL, "mount");
+-}
+-
+-static int apparmor_umount(struct vfsmount *mnt, int flags)
+-{
+-      return aa_reject_syscall(current, GFP_KERNEL, "umount");
+-}
+-
+ static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
+                               struct vfsmount *mnt, int mask)
+ {
+@@ -925,9 +900,6 @@ struct security_operations apparmor_ops
+       .bprm_set_security =            apparmor_bprm_set_security,
+       .bprm_secureexec =              apparmor_bprm_secureexec,
+-      .sb_mount =                     apparmor_sb_mount,
+-      .sb_umount =                    apparmor_umount,
+-
+       .inode_mkdir =                  apparmor_inode_mkdir,
+       .inode_rmdir =                  apparmor_inode_rmdir,
+       .inode_create =                 apparmor_inode_create,
+--- a/security/apparmor/main.c
++++ b/security/apparmor/main.c
+@@ -229,9 +229,13 @@ static int aa_audit_base(struct aa_profi
+               audit_log_format(ab, " protocol=%d", sa->protocol);
+       }
+-        audit_log_format(ab, " pid=%d", current->pid);
++      audit_log_format(ab, " pid=%d", current->pid);
+       if (profile) {
++              if (!sa->parent)
++                      audit_log_format(ab, " parent=%d",
++                                       current->real_parent->pid);
++
+               audit_log_format(ab, " profile=");
+               audit_log_untrustedstring(ab, profile->name);
+@@ -1006,10 +1010,6 @@ repeat:
+               unlock_profile(profile);
+-              if (APPARMOR_COMPLAIN(child_cxt) &&
+-                  profile == profile->ns->null_complain_profile) {
+-                      aa_audit_hint(profile, &sa);
+-              }
+               aa_put_profile(profile);
+       } else
+               aa_free_task_context(child_cxt);
+--- a/security/apparmor/module_interface.c
++++ b/security/apparmor/module_interface.c
+@@ -126,7 +126,7 @@ static int aa_is_nameX(struct aa_ext *e,
+        * AA_NAME tag value is a u16.
+        */
+       if (aa_is_X(e, AA_NAME)) {
+-              char *tag;
++              char *tag = NULL;
+               size_t size = aa_is_u16_chunk(e, &tag);
+               /* if a name is specified it must match. otherwise skip tag */
+               if (name && (!size || strcmp(name, tag)))
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fsetattr-reintro-ATTR_FILE.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fsetattr-reintro-ATTR_FILE.diff
new file mode 100644 (file)
index 0000000..a5efdee
--- /dev/null
@@ -0,0 +1,28 @@
+From: John Johansen <jjohansen@suse.de>
+Subject: AppArmor: reintroduce ATTR_FILE
+
+The fsetattr patch removed ATTR_FILE but AppArmor needs it to distinguish
+file based writes.
+
+Note: Now that LSMs must be static, it would be better to add a file
+pointer argument to security_operations->inode_setattr() instead. Then
+move the fs.h chunk to patches.apparmor/fsetattr-restore-ia_file. -jeffm
+
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ fs/open.c |    3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -208,6 +208,9 @@ int do_truncate(struct dentry *dentry, s
+       newattrs.ia_size = length;
+       newattrs.ia_valid = ATTR_SIZE | time_attrs;
++      if (filp)
++              newattrs.ia_valid |= ATTR_FILE;
++
+       /* Remove suid/sgid on truncate too */
+       newattrs.ia_valid |= should_remove_suid(dentry);
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fsetattr-restore-ia_file.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fsetattr-restore-ia_file.diff
new file mode 100644 (file)
index 0000000..5e5bd12
--- /dev/null
@@ -0,0 +1,58 @@
+From: Jeff Mahoney <jeffm@suse.com>
+Subject: [PATCH] vfs: restore ia_file for compatibility with external modules
+References: bnc#381259
+
+ patches.apparmor/fsetattr.diff eliminated ia_file and ATTR_FILE in favor
+ of providing a ->fsetattr call that used a file pointer. Until this
+ patch is accepted into mainline, this patch provides the backward
+ compatibility for external file system modules.
+
+Signed-off-by: Jeff Mahoney <jeffm@suse.com>
+---
+ fs/attr.c          |   13 ++++++++++++-
+ include/linux/fs.h |   11 +++++++++++
+ 2 files changed, 23 insertions(+), 1 deletion(-)
+
+--- a/fs/attr.c
++++ b/fs/attr.c
+@@ -168,8 +168,19 @@ int fnotify_change(struct dentry *dentry
+               if (!error) {
+                       if (file && file->f_op && file->f_op->fsetattr)
+                               error = file->f_op->fsetattr(file, attr);
+-                      else
++                      else {
++                              /* External file system still expect to be
++                               * passed a file pointer via ia_file and
++                               * have it announced via ATTR_FILE. This
++                               * just makes it so they don't need to
++                               * change their API just for us. External
++                               * callers will have set these themselves. */
++                              if (file) {
++                                      attr->ia_valid |= ATTR_FILE;
++                                      attr->ia_file = file;
++                              }
+                               error = inode->i_op->setattr(dentry, attr);
++                      }
+               }
+       } else {
+               error = inode_change_ok(inode, attr);
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -367,6 +367,17 @@ struct iattr {
+       struct timespec ia_atime;
+       struct timespec ia_mtime;
+       struct timespec ia_ctime;
++
++      /*
++       * Not an attribute, but an auxilary info for filesystems wanting to
++       * implement an ftruncate() like method.  NOTE: filesystem should
++       * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
++       *
++       * NOTE: With patches.apparmor/fsetattr.diff applied, this is
++       * for compatibility with external file system modules only. There
++       * should not be any in-kernel users left.
++       */
++      struct file     *ia_file;
+ };
+ /*
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/fsetattr.diff b/src/patches/suse-2.6.27.39/patches.apparmor/fsetattr.diff
new file mode 100644 (file)
index 0000000..ba01f3f
--- /dev/null
@@ -0,0 +1,414 @@
+Subject: VFS: new fsetattr() file operation
+
+From: Miklos Szeredi <mszeredi@suse.cz>
+
+Add a new file operation: f_op->fsetattr(), that is invoked by
+ftruncate, fchmod, fchown and utimensat.  Fall back to i_op->setattr()
+if it is not defined.
+
+For the reasons why we need this, see patch adding fgetattr().
+
+ftruncate() already passed the open file to the filesystem via the
+ia_file member of struct iattr.  However it is cleaner to have a
+separate file operation for this, so remove ia_file, ATTR_FILE and
+convert existing users: fuse and AFS.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> ---
+Signed-off-by: John Johansen <jjohansen@suse.de> ---
+
+---
+ fs/afs/dir.c       |    1 +
+ fs/afs/file.c      |    1 +
+ fs/afs/inode.c     |   19 +++++++++++++++----
+ fs/afs/internal.h  |    1 +
+ fs/attr.c          |   19 +++++++++++++++----
+ fs/fuse/dir.c      |   20 +++++++++-----------
+ fs/fuse/file.c     |    7 +++++++
+ fs/fuse/fuse_i.h   |    4 ++++
+ fs/open.c          |   20 ++++++++------------
+ fs/utimes.c        |    9 +++++----
+ include/linux/fs.h |    9 ++-------
+ 11 files changed, 68 insertions(+), 42 deletions(-)
+
+--- a/fs/afs/dir.c
++++ b/fs/afs/dir.c
+@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil
+       .release        = afs_release,
+       .readdir        = afs_readdir,
+       .lock           = afs_lock,
++      .fsetattr       = afs_fsetattr,
+ };
+ const struct inode_operations afs_dir_inode_operations = {
+--- a/fs/afs/file.c
++++ b/fs/afs/file.c
+@@ -36,6 +36,7 @@ const struct file_operations afs_file_op
+       .fsync          = afs_fsync,
+       .lock           = afs_lock,
+       .flock          = afs_flock,
++      .fsetattr       = afs_fsetattr,
+ };
+ const struct inode_operations afs_file_inode_operations = {
+--- a/fs/afs/inode.c
++++ b/fs/afs/inode.c
+@@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode
+ /*
+  * set the attributes of an inode
+  */
+-int afs_setattr(struct dentry *dentry, struct iattr *attr)
++static int afs_do_setattr(struct dentry *dentry, struct iattr *attr,
++                 struct file *file)
+ {
+       struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
+       struct key *key;
+@@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s
+               afs_writeback_all(vnode);
+       }
+-      if (attr->ia_valid & ATTR_FILE) {
+-              key = attr->ia_file->private_data;
++      if (file) {
++              key = file->private_data;
+       } else {
+               key = afs_request_key(vnode->volume->cell);
+               if (IS_ERR(key)) {
+@@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s
+       }
+       ret = afs_vnode_setattr(vnode, key, attr);
+-      if (!(attr->ia_valid & ATTR_FILE))
++      if (!file)
+               key_put(key);
+ error:
+       _leave(" = %d", ret);
+       return ret;
+ }
++
++int afs_setattr(struct dentry *dentry, struct iattr *attr)
++{
++      return afs_do_setattr(dentry, attr, NULL);
++}
++
++int afs_fsetattr(struct file *file, struct iattr *attr)
++{
++      return afs_do_setattr(file->f_path.dentry, attr, file);
++}
+--- a/fs/afs/internal.h
++++ b/fs/afs/internal.h
+@@ -548,6 +548,7 @@ extern void afs_zap_data(struct afs_vnod
+ extern int afs_validate(struct afs_vnode *, struct key *);
+ extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
+ extern int afs_setattr(struct dentry *, struct iattr *);
++extern int afs_fsetattr(struct file *, struct iattr *);
+ extern void afs_clear_inode(struct inode *);
+ /*
+--- a/fs/attr.c
++++ b/fs/attr.c
+@@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode,
+ }
+ EXPORT_SYMBOL(inode_setattr);
+-int notify_change(struct dentry *dentry, struct vfsmount *mnt,
+-                struct iattr *attr)
++int fnotify_change(struct dentry *dentry, struct vfsmount *mnt,
++                 struct iattr *attr, struct file *file)
+ {
+       struct inode *inode = dentry->d_inode;
+       mode_t mode = inode->i_mode;
+@@ -165,8 +165,12 @@ int notify_change(struct dentry *dentry,
+       if (inode->i_op && inode->i_op->setattr) {
+               error = security_inode_setattr(dentry, mnt, attr);
+-              if (!error)
+-                      error = inode->i_op->setattr(dentry, attr);
++              if (!error) {
++                      if (file && file->f_op && file->f_op->fsetattr)
++                              error = file->f_op->fsetattr(file, attr);
++                      else
++                              error = inode->i_op->setattr(dentry, attr);
++              }
+       } else {
+               error = inode_change_ok(inode, attr);
+               if (!error)
+@@ -188,5 +192,12 @@ int notify_change(struct dentry *dentry,
+       return error;
+ }
++EXPORT_SYMBOL_GPL(fnotify_change);
++
++int notify_change(struct dentry *dentry, struct vfsmount *mnt,
++                struct iattr *attr)
++{
++      return fnotify_change(dentry, mnt, attr, NULL);
++}
+ EXPORT_SYMBOL(notify_change);
+--- a/fs/fuse/dir.c
++++ b/fs/fuse/dir.c
+@@ -1105,21 +1105,22 @@ static int fuse_dir_fsync(struct file *f
+       return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
+ }
+-static bool update_mtime(unsigned ivalid)
++static bool update_mtime(unsigned ivalid, bool have_file)
+ {
+       /* Always update if mtime is explicitly set  */
+       if (ivalid & ATTR_MTIME_SET)
+               return true;
+       /* If it's an open(O_TRUNC) or an ftruncate(), don't update */
+-      if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
++      if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file))
+               return false;
+       /* In all other cases update */
+       return true;
+ }
+-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
++static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
++                         bool have_file)
+ {
+       unsigned ivalid = iattr->ia_valid;
+@@ -1138,7 +1139,7 @@ static void iattr_to_fattr(struct iattr
+               if (!(ivalid & ATTR_ATIME_SET))
+                       arg->valid |= FATTR_ATIME_NOW;
+       }
+-      if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
++      if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) {
+               arg->valid |= FATTR_MTIME;
+               arg->mtime = iattr->ia_mtime.tv_sec;
+               arg->mtimensec = iattr->ia_mtime.tv_nsec;
+@@ -1199,8 +1200,8 @@ void fuse_release_nowrite(struct inode *
+  * vmtruncate() doesn't allow for this case, so do the rlimit checking
+  * and the actual truncation by hand.
+  */
+-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+-                         struct file *file)
++int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
++                  struct file *file)
+ {
+       struct inode *inode = entry->d_inode;
+       struct fuse_conn *fc = get_fuse_conn(inode);
+@@ -1244,7 +1245,7 @@ static int fuse_do_setattr(struct dentry
+       memset(&inarg, 0, sizeof(inarg));
+       memset(&outarg, 0, sizeof(outarg));
+-      iattr_to_fattr(attr, &inarg);
++      iattr_to_fattr(attr, &inarg, file != NULL);
+       if (file) {
+               struct fuse_file *ff = file->private_data;
+               inarg.valid |= FATTR_FH;
+@@ -1314,10 +1315,7 @@ error:
+ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
+ {
+-      if (attr->ia_valid & ATTR_FILE)
+-              return fuse_do_setattr(entry, attr, attr->ia_file);
+-      else
+-              return fuse_do_setattr(entry, attr, NULL);
++      return fuse_do_setattr(entry, attr, NULL);
+ }
+ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
+--- a/fs/fuse/file.c
++++ b/fs/fuse/file.c
+@@ -1467,6 +1467,11 @@ static loff_t fuse_file_llseek(struct fi
+       return retval;
+ }
++static int fuse_fsetattr(struct file *file, struct iattr *attr)
++{
++      return fuse_do_setattr(file->f_path.dentry, attr, file);
++}
++
+ static const struct file_operations fuse_file_operations = {
+       .llseek         = fuse_file_llseek,
+       .read           = do_sync_read,
+@@ -1480,6 +1485,7 @@ static const struct file_operations fuse
+       .fsync          = fuse_fsync,
+       .lock           = fuse_file_lock,
+       .flock          = fuse_file_flock,
++      .fsetattr       = fuse_fsetattr,
+       .splice_read    = generic_file_splice_read,
+ };
+@@ -1493,6 +1499,7 @@ static const struct file_operations fuse
+       .fsync          = fuse_fsync,
+       .lock           = fuse_file_lock,
+       .flock          = fuse_file_flock,
++      .fsetattr       = fuse_fsetattr,
+       /* no mmap and splice_read */
+ };
+--- a/fs/fuse/fuse_i.h
++++ b/fs/fuse/fuse_i.h
+@@ -551,6 +551,10 @@ void fuse_truncate(struct address_space
+  */
+ int fuse_dev_init(void);
++
++int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
++                  struct file *file);
++
+ /**
+  * Cleanup the client device
+  */
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -207,16 +207,12 @@ int do_truncate(struct dentry *dentry, s
+       newattrs.ia_size = length;
+       newattrs.ia_valid = ATTR_SIZE | time_attrs;
+-      if (filp) {
+-              newattrs.ia_file = filp;
+-              newattrs.ia_valid |= ATTR_FILE;
+-      }
+       /* Remove suid/sgid on truncate too */
+       newattrs.ia_valid |= should_remove_suid(dentry);
+       mutex_lock(&dentry->d_inode->i_mutex);
+-      err = notify_change(dentry, mnt, &newattrs);
++      err = fnotify_change(dentry, mnt, &newattrs, filp);
+       mutex_unlock(&dentry->d_inode->i_mutex);
+       return err;
+ }
+@@ -625,7 +621,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
+               mode = inode->i_mode;
+       newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+       newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+-      err = notify_change(dentry, file->f_path.mnt, &newattrs);
++      err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
+       mutex_unlock(&inode->i_mutex);
+       mnt_drop_write(file->f_path.mnt);
+ out_putf:
+@@ -669,7 +665,7 @@ SYSCALL_DEFINE2(chmod, const char __user
+ }
+ static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
+-                      uid_t user, gid_t group)
++                      uid_t user, gid_t group, struct file *file)
+ {
+       struct inode *inode = dentry->d_inode;
+       int error;
+@@ -688,7 +684,7 @@ static int chown_common(struct dentry *
+               newattrs.ia_valid |=
+                       ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+       mutex_lock(&inode->i_mutex);
+-      error = notify_change(dentry, mnt, &newattrs);
++      error = fnotify_change(dentry, mnt, &newattrs, file);
+       mutex_unlock(&inode->i_mutex);
+       return error;
+@@ -705,7 +701,7 @@ SYSCALL_DEFINE3(chown, const char __user
+       error = mnt_want_write(path.mnt);
+       if (error)
+               goto out_release;
+-      error = chown_common(path.dentry, path.mnt, user, group);
++      error = chown_common(path.dentry, path.mnt, user, group, NULL);
+       mnt_drop_write(path.mnt);
+ out_release:
+       path_put(&path);
+@@ -730,7 +726,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
+       error = mnt_want_write(path.mnt);
+       if (error)
+               goto out_release;
+-      error = chown_common(path.dentry, path.mnt, user, group);
++      error = chown_common(path.dentry, path.mnt, user, group, NULL);
+       mnt_drop_write(path.mnt);
+ out_release:
+       path_put(&path);
+@@ -749,7 +745,7 @@ SYSCALL_DEFINE3(lchown, const char __use
+       error = mnt_want_write(path.mnt);
+       if (error)
+               goto out_release;
+-      error = chown_common(path.dentry, path.mnt, user, group);
++      error = chown_common(path.dentry, path.mnt, user, group, NULL);
+       mnt_drop_write(path.mnt);
+ out_release:
+       path_put(&path);
+@@ -772,7 +768,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
+               goto out_fput;
+       dentry = file->f_path.dentry;
+       audit_inode(NULL, dentry);
+-      error = chown_common(dentry, file->f_path.mnt, user, group);
++      error = chown_common(dentry, file->f_path.mnt, user, group, file);
+       mnt_drop_write(file->f_path.mnt);
+ out_fput:
+       fput(file);
+--- a/fs/utimes.c
++++ b/fs/utimes.c
+@@ -48,7 +48,8 @@ static bool nsec_valid(long nsec)
+       return nsec >= 0 && nsec <= 999999999;
+ }
+-static int utimes_common(struct path *path, struct timespec *times)
++static int utimes_common(struct path *path, struct timespec *times,
++                       struct file *f)
+ {
+       int error;
+       struct iattr newattrs;
+@@ -102,7 +103,7 @@ static int utimes_common(struct path *pa
+               }
+       }
+       mutex_lock(&inode->i_mutex);
+-      error = notify_change(path->dentry, path->mnt, &newattrs);
++      error = fnotify_change(path->dentry, path->mnt, &newattrs, f);
+       mutex_unlock(&inode->i_mutex);
+ mnt_drop_write_and_out:
+@@ -149,7 +150,7 @@ long do_utimes(int dfd, char __user *fil
+               if (!file)
+                       goto out;
+-              error = utimes_common(&file->f_path, times);
++              error = utimes_common(&file->f_path, times, file);
+               fput(file);
+       } else {
+               struct path path;
+@@ -162,7 +163,7 @@ long do_utimes(int dfd, char __user *fil
+               if (error)
+                       goto out;
+-              error = utimes_common(&path, times);
++              error = utimes_common(&path, times, NULL);
+               path_put(&path);
+       }
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -367,13 +367,6 @@ struct iattr {
+       struct timespec ia_atime;
+       struct timespec ia_mtime;
+       struct timespec ia_ctime;
+-
+-      /*
+-       * Not an attribute, but an auxilary info for filesystems wanting to
+-       * implement an ftruncate() like method.  NOTE: filesystem should
+-       * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
+-       */
+-      struct file     *ia_file;
+ };
+ /*
+@@ -1280,6 +1273,7 @@ struct file_operations {
+ #define HAVE_FOP_OPEN_EXEC
+       int (*open_exec) (struct inode *);
+       int (*setlease)(struct file *, long, struct file_lock **);
++      int (*fsetattr)(struct file *, struct iattr *);
+ };
+ struct inode_operations {
+@@ -1799,6 +1793,7 @@ extern int do_remount_sb(struct super_bl
+ extern sector_t bmap(struct inode *, sector_t);
+ #endif
+ extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
++extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *);
+ extern int inode_permission(struct inode *, int);
+ extern int generic_permission(struct inode *, int,
+               int (*check_acl)(struct inode *, int));
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/remove_suid.diff b/src/patches/suse-2.6.27.39/patches.apparmor/remove_suid.diff
new file mode 100644 (file)
index 0000000..9c639fa
--- /dev/null
@@ -0,0 +1,41 @@
+From: Andreas Gruenbacher <agruen@suse.de>
+Subject: Pass struct path down to remove_suid and children
+
+Required by a later patch that adds a struct vfsmount parameter to
+notify_change().
+
+Signed-off-by: Tony Jones <tonyj@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+
+ mm/filemap.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/mm/filemap.c
++++ b/mm/filemap.c
+@@ -1826,12 +1826,12 @@ int should_remove_suid(struct dentry *de
+ }
+ EXPORT_SYMBOL(should_remove_suid);
+-static int __remove_suid(struct dentry *dentry, int kill)
++static int __remove_suid(struct path *path, int kill)
+ {
+       struct iattr newattrs;
+       newattrs.ia_valid = ATTR_FORCE | kill;
+-      return notify_change(dentry, &newattrs);
++      return notify_change(path->dentry, &newattrs);
+ }
+ int file_remove_suid(struct file *file)
+@@ -1846,7 +1846,7 @@ int file_remove_suid(struct file *file)
+       if (killpriv)
+               error = security_inode_killpriv(dentry);
+       if (!error && killsuid)
+-              error = __remove_suid(dentry, killsuid);
++              error = __remove_suid(&file->f_path, killsuid);
+       return error;
+ }
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/security-create.diff b/src/patches/suse-2.6.27.39/patches.apparmor/security-create.diff
new file mode 100644 (file)
index 0000000..9d16017
--- /dev/null
@@ -0,0 +1,107 @@
+From: Tony Jones <tonyj@suse.de>
+Subject: Pass struct vfsmount to the inode_create LSM hook
+
+This is needed for computing pathnames in the AppArmor LSM.
+
+Signed-off-by: Tony Jones <tonyj@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ fs/namei.c               |    2 +-
+ include/linux/security.h |    9 ++++++---
+ security/capability.c    |    2 +-
+ security/security.c      |    5 +++--
+ security/selinux/hooks.c |    3 ++-
+ 5 files changed, 13 insertions(+), 8 deletions(-)
+
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -1543,7 +1543,7 @@ int vfs_create(struct inode *dir, struct
+               return -EACCES; /* shouldn't it be ENOSYS? */
+       mode &= S_IALLUGO;
+       mode |= S_IFREG;
+-      error = security_inode_create(dir, dentry, mode);
++      error = security_inode_create(dir, dentry, nd ? nd->path.mnt : NULL, mode);
+       if (error)
+               return error;
+       DQUOT_INIT(dir);
+--- a/include/linux/security.h
++++ b/include/linux/security.h
+@@ -337,6 +337,7 @@ static inline void security_free_mnt_opt
+  *    Check permission to create a regular file.
+  *    @dir contains inode structure of the parent of the new file.
+  *    @dentry contains the dentry structure for the file to be created.
++ *    @mnt is the vfsmount corresponding to @dentry (may be NULL).
+  *    @mode contains the file mode of the file to be created.
+  *    Return 0 if permission is granted.
+  * @inode_link:
+@@ -1354,8 +1355,8 @@ struct security_operations {
+       void (*inode_free_security) (struct inode *inode);
+       int (*inode_init_security) (struct inode *inode, struct inode *dir,
+                                   char **name, void **value, size_t *len);
+-      int (*inode_create) (struct inode *dir,
+-                           struct dentry *dentry, int mode);
++      int (*inode_create) (struct inode *dir, struct dentry *dentry,
++                           struct vfsmount *mnt, int mode);
+       int (*inode_link) (struct dentry *old_dentry,
+                          struct inode *dir, struct dentry *new_dentry);
+       int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
+@@ -1622,7 +1623,8 @@ int security_inode_alloc(struct inode *i
+ void security_inode_free(struct inode *inode);
+ int security_inode_init_security(struct inode *inode, struct inode *dir,
+                                 char **name, void **value, size_t *len);
+-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
++int security_inode_create(struct inode *dir, struct dentry *dentry,
++                        struct vfsmount *mnt, int mode);
+ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
+                        struct dentry *new_dentry);
+ int security_inode_unlink(struct inode *dir, struct dentry *dentry);
+@@ -1968,6 +1970,7 @@ static inline int security_inode_init_se
+ static inline int security_inode_create(struct inode *dir,
+                                        struct dentry *dentry,
++                                       struct vfsmount *mnt,
+                                        int mode)
+ {
+       return 0;
+--- a/security/capability.c
++++ b/security/capability.c
+@@ -155,7 +155,7 @@ static int cap_inode_init_security(struc
+ }
+ static int cap_inode_create(struct inode *inode, struct dentry *dentry,
+-                          int mask)
++                          struct vfsmount *mnt, int mask)
+ {
+       return 0;
+ }
+--- a/security/security.c
++++ b/security/security.c
+@@ -355,11 +355,12 @@ int security_inode_init_security(struct
+ }
+ EXPORT_SYMBOL(security_inode_init_security);
+-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
++int security_inode_create(struct inode *dir, struct dentry *dentry,
++                        struct vfsmount *mnt, int mode)
+ {
+       if (unlikely(IS_PRIVATE(dir)))
+               return 0;
+-      return security_ops->inode_create(dir, dentry, mode);
++      return security_ops->inode_create(dir, dentry, mnt, mode);
+ }
+ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -2566,7 +2566,8 @@ static int selinux_inode_init_security(s
+       return 0;
+ }
+-static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
++static int selinux_inode_create(struct inode *dir, struct dentry *dentry,
++                               struct vfsmount *mnt, int mask)
+ {
+       return may_create(dir, dentry, SECCLASS_FILE);
+ }
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/security-getxattr.diff b/src/patches/suse-2.6.27.39/patches.apparmor/security-getxattr.diff
new file mode 100644 (file)
index 0000000..0e98d57
--- /dev/null
@@ -0,0 +1,128 @@
+From: Tony Jones <tonyj@suse.de>
+Subject: Pass struct vfsmount to the inode_getxattr LSM hook
+
+This is needed for computing pathnames in the AppArmor LSM.
+
+Signed-off-by: Tony Jones <tonyj@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ fs/xattr.c                 |    2 +-
+ include/linux/security.h   |   11 +++++++----
+ security/capability.c      |    3 ++-
+ security/security.c        |    5 +++--
+ security/selinux/hooks.c   |    3 ++-
+ security/smack/smack_lsm.c |    4 +++-
+ 6 files changed, 18 insertions(+), 10 deletions(-)
+
+--- a/fs/xattr.c
++++ b/fs/xattr.c
+@@ -141,7 +141,7 @@ vfs_getxattr(struct dentry *dentry, stru
+       if (error)
+               return error;
+-      error = security_inode_getxattr(dentry, name);
++      error = security_inode_getxattr(dentry, mnt, name);
+       if (error)
+               return error;
+--- a/include/linux/security.h
++++ b/include/linux/security.h
+@@ -446,7 +446,7 @@ static inline void security_free_mnt_opt
+  *    @value identified by @name for @dentry and @mnt.
+  * @inode_getxattr:
+  *    Check permission before obtaining the extended attributes
+- *    identified by @name for @dentry.
++ *    identified by @name for @dentry and @mnt.
+  *    Return 0 if permission is granted.
+  * @inode_listxattr:
+  *    Check permission before obtaining the list of extended attribute
+@@ -1400,7 +1400,8 @@ struct security_operations {
+                                    struct vfsmount *mnt,
+                                    const char *name, const void *value,
+                                    size_t size, int flags);
+-      int (*inode_getxattr) (struct dentry *dentry, const char *name);
++      int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
++                             const char *name);
+       int (*inode_listxattr) (struct dentry *dentry);
+       int (*inode_removexattr) (struct dentry *dentry, const char *name);
+       int (*inode_need_killpriv) (struct dentry *dentry);
+@@ -1676,7 +1677,8 @@ int security_inode_setxattr(struct dentr
+ void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+                                 const char *name, const void *value,
+                                 size_t size, int flags);
+-int security_inode_getxattr(struct dentry *dentry, const char *name);
++int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
++                          const char *name);
+ int security_inode_listxattr(struct dentry *dentry);
+ int security_inode_removexattr(struct dentry *dentry, const char *name);
+ int security_inode_need_killpriv(struct dentry *dentry);
+@@ -2113,7 +2115,8 @@ static inline void security_inode_post_s
+ { }
+ static inline int security_inode_getxattr(struct dentry *dentry,
+-                      const char *name)
++                                        struct vfsmount *mnt,
++                                        const char *name)
+ {
+       return 0;
+ }
+--- a/security/capability.c
++++ b/security/capability.c
+@@ -241,7 +241,8 @@ static void cap_inode_post_setxattr(stru
+ {
+ }
+-static int cap_inode_getxattr(struct dentry *dentry, const char *name)
++static int cap_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
++                            const char *name)
+ {
+       return 0;
+ }
+--- a/security/security.c
++++ b/security/security.c
+@@ -488,11 +488,12 @@ void security_inode_post_setxattr(struct
+                                         flags);
+ }
+-int security_inode_getxattr(struct dentry *dentry, const char *name)
++int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
++                          const char *name)
+ {
+       if (unlikely(IS_PRIVATE(dentry->d_inode)))
+               return 0;
+-      return security_ops->inode_getxattr(dentry, name);
++      return security_ops->inode_getxattr(dentry, mnt, name);
+ }
+ int security_inode_listxattr(struct dentry *dentry)
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -2796,7 +2796,8 @@ static void selinux_inode_post_setxattr(
+       return;
+ }
+-static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
++static int selinux_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
++                                const char *name)
+ {
+       return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+ }
+--- a/security/smack/smack_lsm.c
++++ b/security/smack/smack_lsm.c
+@@ -673,11 +673,13 @@ static void smack_inode_post_setxattr(st
+ /*
+  * smack_inode_getxattr - Smack check on getxattr
+  * @dentry: the object
++ * @mnt: unused
+  * @name: unused
+  *
+  * Returns 0 if access is permitted, an error code otherwise
+  */
+-static int smack_inode_getxattr(struct dentry *dentry, const char *name)
++static int smack_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
++                              const char *name)
+ {
+       return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+ }
diff --git a/src/patches/suse-2.6.27.39/patches.apparmor/security-link.diff b/src/patches/suse-2.6.27.39/patches.apparmor/security-link.diff
new file mode 100644 (file)
index 0000000..b43e1e6
--- /dev/null
@@ -0,0 +1,149 @@
+From: Tony Jones <tonyj@suse.de>
+Subject: Pass the struct vfsmounts to the inode_link LSM hook
+
+This is needed for computing pathnames in the AppArmor LSM.
+
+Signed-off-by: Tony Jones <tonyj@suse.de>
+Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
+Signed-off-by: John Johansen <jjohansen@suse.de>
+
+---
+ fs/namei.c                 |